
Klant: Useq
Project: Spatial Organ Atlas
Dataset: Kidney
date: 09 25 Thu 16 March, 2023

loading dependencies Please make sure the following
packages are installed and required libraries can be loaded:
- install.packages(“pkgbuild”) // pkgbuild::check_build_tools()
- install.packages(“devtools”)
- devtools::install_github(“Nanostring-Biostats/NanoStringNCTools”)
- devtools::install_github(“Nanostring-Biostats/GeomxTools”, ref =
“dev”)
- BiocManager::install(“GeoMxWorkflows”)
- devtools::install_github(“DavisLaboratory/standR”)
- BiocManager::install(“SpatialDecon”)
- BiocManager::install(“GSVA”)
- install.packages(“plotly”)
- install.packages(“DT”)
- install.packages(“msigdbr”)
- install.packages(“digest”)
- install.packages(“rmarkdown”)
- install.packages(“kable”)
- BiocManager::install(“EBImage”)
- install.packages(‘scattermore’)
- install.packages(‘pbapply’)
- install.packages(‘plotrix’)
- install.packages(‘ggtext’)
- install.packages(‘RBioFormats’)
- BiocManager::install(“aoles/RBioFormats”)
- install.packages(“C:/Users/inijman/Downloads/SpatialOmicsOverlay-0.99.13-beta.tar.gz”,
dependencies = TRUE, repos = NULL)
- devtools::install_github(“DavisLaboratory/standR”)
- BiocManager::install(“clusterProfiler”)
- BiocManager::install(“pathview”)
#load libraries
#stack_size <- getOption("pandoc.stack.size", default = "512m")
#options(java.parameters = c("-XX:+UseConcMarkSweepGC", "-Xmx8192m, -XX:MetaspaceSize=1024M"))
options(java.parameters = c("-XX:+UseConcMarkSweepGC", "-Xmx8192m"))
library(NanoStringNCTools)
library(GeomxTools)
library(GeoMxWorkflows)
library(SpatialDecon)
library(GSVA) #for pathway analyses
library(msigdbr) #for molecular signatures in pathway analyses
library(knitr)
library(dplyr)
library(ggforce)
library(ggplot2)
library(scales) # for percent
library(reshape2) # for melt
library(cowplot) # for plot_grid
library(umap)
library(Rtsne)
library(pheatmap) # for pheatmap
library(ggrepel)
library(scales) #for ggplot peaudolog to prevent errors on log(0)
library(DT)
library(plotly)
library(gridExtra)
library(RColorBrewer)
library(SpatialOmicsOverlay)
library(gt)
library(tidyverse)
library(clusterProfiler)
library(fgsea)
library(pathview)
library(png)
library(readxl)
library(viridis)
library(standR)
library(SpatialExperiment)
library(limma)
library(ggalluvial)
library(scater)
#BiocManager::install("sva")
library(sva)
#BiocManager::install("preprocessCore") #quantile norm
library(preprocessCore)
#install.packages("Polychrome") #Get colors
library(Polychrome)
library(infercnv) # CNV
library("ape")
library("Biostrings")
library("ggtree")
library(tidytree)
1 loading base files
# Reference the main folder 'file.path' containing the sub-folders with each data file type:
datadir<-file.path("L:/pkloosterman/Github/DKD_Kidney/")
To locate a specific file path replace the above line with datadir
<- file.path(“~/Folder/SubFolder/DataLocation”) replace the Folder,
SubFolder, DataLocation as needed. The DataLocation folder should
contain a dccs, pkcs, and annotation folder with each set of files
present as needed automatically list files in each directory for
use.
Take care you import a column with nuclei count separately if
you want.
DCCFiles <- dir(file.path(datadir, "dccs"), pattern = ".dcc$",
full.names = TRUE, recursive = TRUE)
PKCFiles <- dir(file.path(datadir, "pkcs"), pattern = ".pkc$",
full.names = TRUE, recursive = TRUE)
SampleAnnotationFile <-
dir(file.path(datadir, "annotation"), pattern = "^[^~]",
full.names = TRUE, recursive = TRUE)
2 load data
Data <-
readNanoStringGeoMxSet(dccFiles = DCCFiles,
pkcFiles = PKCFiles,
phenoDataFile = SampleAnnotationFile,
phenoDataSheet = "Template",
phenoDataDccColName = "Sample_ID",
protocolDataColNames = c("aoi", "roi"),
experimentDataColNames = c("panel"))
#save data to prevent loading time for retakes
#saveData<-Data
#Data<-saveData
#change Data column names and manual correction of spelling errors
Data@phenoData@data[["slide_name"]]<-Data@phenoData@data[["slide name"]]
Data@phenoData@data[["slide name"]]<- NULL
#+1 references the slide name column
ann_size<-length(colnames(Data@phenoData@data)[grepl("ANN",colnames(Data@phenoData@data))])+1
ann_names<-c(colnames(Data@phenoData@data)[grepl("ANN",colnames(Data@phenoData@data))],"slide_name")
# Feel free to change the order of which colors are appointed.
color<-c("#A349A4", "#FFFF33", "#E7298A", "#091833", "#1B9E77", "#D95F02", "#7570B3", "#66A61E", "#E6AB02", "#8DD3C7", "#9F000F", "#BEBADA", "#FB8072", "#80B1D3", "#FDB462", "#B3DE69", "#FCCDE5", "#D9D9D9", "#BC80BD", "#CCEBC5", "#FFED6F", "#377EB8", "#984EA3", "#4DAF4A", "#FF71CE", "#FF7F00", "#A6CEE3", "#1F78B4", "#B2DF8A", "#33A02C", "#FB9A99", "#E31A1C", "#FDBF6F", "#CAB2D6", "#6A3D9A", "#FFFF99", "#B15928")
# Use count and count_max to set the range of color needed for each column.
# With setNames() the color code is linked to each unique individual value of each column.
count=1
color_list = list()
for (ann in ann_names) {
count_max = count+length(unique(Data@phenoData@data[[ann]]))-1
color_list[[ann]]<-setNames(color[count:count_max], unique(Data@phenoData@data[[ann]]))
count=count_max+1
}
value_names = c()
for (ann in ann_names) {
value_names <- c(value_names, unique(Data@phenoData@data[[ann]]))
}
color_df <- as.data.frame(value_names)
color_df$color <- color[0:length(value_names)]
color_df %>%
mutate(color = fct_inorder(color)) |>
gt() %>%
data_color(columns = color, colors = as.character(color)) %>%
tab_options(container.height = 500)
| value_names |
color |
| DKD |
#A349A4 |
| normal |
#FFFF33 |
| disease3 |
#E7298A |
| disease4 |
#091833 |
| normal3 |
#1B9E77 |
| normal4 |
#D95F02 |
| disease1B |
#7570B3 |
| disease2B |
#66A61E |
| normal2B |
#E6AB02 |
| glomerulus |
#8DD3C7 |
| tubule |
#9F000F |
| abnormal |
#BEBADA |
| NA |
#FB8072 |
| normal |
#80B1D3 |
| niormal |
#FDB462 |
| disease3 |
#B3DE69 |
| disease4 |
#FCCDE5 |
| normal3 |
#D9D9D9 |
| normal4 |
#BC80BD |
| disease1B |
#CCEBC5 |
| disease2B |
#FFED6F |
| normal2B |
#377EB8 |
paste("Reads from following runs used: ",unique(pData(protocolData(Data))$SeqSetId))
## [1] "Reads from following runs used: "
3 Study design
pkcs <- annotation(Data)
modules <- gsub(".pkc", "", pkcs)
kable(data.frame(PKCs = pkcs, modules = modules))
| TAP_H_WTA_v1.0.pkc |
TAP_H_WTA_v1.0 |
Select the annotations we want to show, use `` to surround column
names with spaces or special symbols
#count_mat <- dplyr::count(pData(Data), ANN1,ANN2,slide_name)
count_mat <- pData(Data) %>% dplyr::count(pData(Data)[c(ann_names)])
Simplify the slide names if required
# count_mat$slide_name <- gsub("disease", "d", gsub("normal", "n", count_mat$slide_name))
#count_mat$path_ann <- gsub("i", "", count_mat$path_ann) #correcting spelling error
Gather the data and plot in order: class, slide name, region,
segment
test_gr <- gather_set_data(count_mat, 1:all_of(ann_size))
test_gr$x <- factor(test_gr$x, labels = ann_names)
Plot Sankey
ggplot(test_gr, height = 10, width = 10, aes(x, id = id, split = y, value = n)) +
geom_parallel_sets(aes(fill = ANN2), alpha = 0.5, axis.width = 0.1) +
geom_parallel_sets_axes(axis.width = 0.2) +
geom_parallel_sets_labels(color = "white", size = 5) +
theme_classic(base_size = 12) +
theme(legend.position = "bottom",
axis.ticks.y = element_blank(),
axis.line = element_blank(),
axis.text.y = element_blank()) +
scale_y_continuous(expand = expansion(0)) +
scale_x_discrete(expand = expansion(0)) +
labs(x = "", y = "") +
scale_fill_manual(values=color_list$ANN2) +
annotate(geom = "segment", x = 4.25, xend = 4.25,
y = 10, yend = 61, lwd = 2) +
annotate(geom = "text", x = 4.19, y = 25, angle = 90, size = 5,
hjust = 0.5, label = "50 segments")

4 QC & Pre-processing
Shift counts to one
#shift any expression counts with a value of 0 to 1 to enable in downstream transformations.
Data <- shiftCountsOne(Data, useDALogic = TRUE)
4.1 Segment QC
We first assess sequencing quality and adequate tissue sampling for
every ROI/AOI segment.
Every ROI/AOI segment will be tested for:
Raw sequencing reads: segments with >1000 raw reads are removed. %
Aligned,% Trimmed, or % Stitched sequencing reads: segments below ~80%
for one or more of these QC parameters are removed. % Sequencing
saturation ([1-deduplicated reads/aligned reads]%): segments below ~50%
require additional sequencing to capture full sample diversity and are
not typically analyzed until improved. Negative Count: this is the
geometric mean of the several unique negative probes in the GeoMx panel
that do not target mRNA and establish the background count level per
segment; segments with low negative counts (1-10) are not necessarily
removed but may be studied closer for low endogenous gene signal and/or
insufficient tissue sampling. No Template Control (NTC) count: values
>1,000 could indicate contamination for the segments associated with
this NTC; however, in cases where the NTC count is between 1,000-
10,000, the segments may be used if the NTC data is uniformly low (e.g.
0-2 counts for all probes). Nuclei: >100 nuclei per segment is
generally recommended; however, this cutoff is highly study/tissue
dependent and may need to be reduced; what is most important is
consistency in the nuclei distribution for segments within the study.
Area: generally correlates with nuclei; a strict cutoff is not generally
applied based on area.
4.1.1 Select Segment QC
First, we select the QC parameter cutoffs, against which our ROI/AOI
segments will be tested and flagged appropriately. We have selected the
appropriate study-specific parameters for this study. Note: the default
QC values recommended above are advised when surveying a new dataset for
the first time.
Default QC cutoffs are commented in () adjacent to the respective
parameters study-specific values were selected after visualizing the QC
results in more detail below
QC_params <-
list(minSegmentReads = 1000, # Minimum number of reads (1000)
percentTrimmed = 80, # Minimum % of reads trimmed (80%)
percentStitched = 80, # Minimum % of reads stitched (80%)
percentAligned = 75, # Minimum % of reads aligned (80%)
percentSaturation = 50, # Minimum sequencing saturation (50%)
minNegativeCount = 1, # Minimum negative control counts (10)
maxNTCCount = 9000, # Maximum counts observed in NTC well (1000)
minNuclei = 20, # Minimum # of nuclei estimated (100)
minArea = 1000) # Minimum segment area (5000)
Data <-
setSegmentQCFlags(Data, qcCutoffs = QC_params)
cat("pre-QC features:", dim(Data)[1], "\npre-QC samples:", dim(Data)[2])
## pre-QC features: 18642
## pre-QC samples: 276
#Table for clarification
QCparams_df <- data.frame (
items = c("minSegmentReads","percentTrimmed","percentStitched","percentAligned","percentSaturation",
"minNegativeCount","maxNTCCount","minNuclei","minArea"),
defaults = c(1000,80,80,80,50,10,1000,100,5000),
actual = c(QC_params$minSegmentReads,QC_params$percentTrimmed,QC_params$percentStitched,QC_params$percentAligned,QC_params$percentSaturation,QC_params$minNegativeCount,QC_params$maxNTCCount,QC_params$minNuclei,QC_params$minArea)
)
datatable(QCparams_df, rownames=FALSE,
caption = "QC thresholds",
extensions = 'Buttons', options = list (
dom = 'Bftrip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
)
)
Collate QC Results
QCResults <- protocolData(Data)[["QCFlags"]]
flag_columns <- colnames(QCResults)
QC_Summary <- data.frame(Pass = colSums(!QCResults[, flag_columns]),
Warning = colSums(QCResults[, flag_columns]))
QCResults$QCStatus <- apply(QCResults, 1L, function(x) {
ifelse(sum(x) == 0L, "PASS", "WARNING")
})
QC_Summary["TOTAL FLAGS", ] <-
c(sum(QCResults[, "QCStatus"] == "PASS"),
sum(QCResults[, "QCStatus"] == "WARNING"))
col_by <- "ANN1"
col_by_plate <- "Plate_ID"
4.2 Graphical summaries of QC
statistics
Use the tab-menu to navigate!
QC_histogram <- function(assay_data = NULL,
annotation = NULL,
fill_by = NULL,
thr = NULL,
scale_trans = NULL) {
plt <- ggplot(assay_data,
aes_string(x = paste0("unlist(`", annotation, "`)"),
fill = fill_by)) +
geom_histogram(bins = 50) +
geom_vline(xintercept = thr, lty = "dashed", color = "black") +
theme_bw() + guides(fill = "none") +
facet_wrap(as.formula(paste("~", fill_by)), nrow = 4) +
scale_fill_manual(values=color_list$ANN1) +
labs(x = annotation, y = "segments, #", title = annotation)
if(!is.null(scale_trans)) {
plt <- plt +
scale_x_continuous(trans = scale_trans)
}
plt
}
Trimmed
QC_histogram(sData(Data), "Trimmed (%)", col_by, QC_params$percentTrimmed)

Stiched (%)
QC_histogram(sData(Data), "Stitched (%)", col_by, QC_params$percentStitched)

Aligned (%)
QC_histogram(sData(Data), "Aligned (%)", col_by,QC_params$percentAligned)

Sequencing Saturation (%)
QC_histogram(sData(Data), "Saturated (%)", col_by, QC_params$percentSaturation) +
labs(title = "Sequencing Saturation (%)",
x = "Sequencing Saturation (%)")

Area
QC_histogram(sData(Data), "area", col_by, QC_params$minArea, scale_trans = "log10")

Nuclei count
QC_histogram(sData(Data), "nuclei", col_by, QC_params$minNuclei)

DuplicationRate
ggplot(pData(protocolData(Data)),
aes(x = Plate_ID, fill=Plate_ID,
y = (DeduplicatedReads/Raw))) +
geom_violin() +
geom_jitter(width = .2) +
labs(y = "Deduplicated / Raw reads") +
scale_y_continuous(labels = scales::percent) +
theme_bw()

Negprobes vs Endogenous
tmp_target_Data <- aggregateCounts(Data)
#get negative probe data
negs<-subset(tmp_target_Data,CodeClass=="Negative")
p1<-ggplot(pData(negs),
aes(x = ANN2, fill = ANN2,
y = assayDataElement(negs, elt = "exprs"))) +
geom_violin() +
geom_jitter(width = .2) +
labs(y = "Negative probes Expression") +
scale_y_continuous(limits = c(1,3000), trans = "log2") +
theme_bw() + coord_flip()
# get endogenous probe data
end<-subset(tmp_target_Data,CodeClass=="Endogenous")
p2<-ggplot(pData(end),
aes(x = ANN2, fill = ANN2,
y = colMeans(assayDataElement(end, elt = "exprs")))) +
geom_violin() +
geom_jitter(width = .2) +
labs(y = "Endogenous probes Expression (mean)") +
scale_y_continuous(limits = c(1,3000),trans = "log2") +
theme_bw() + coord_flip()
pl <-list(p1,p2)
plot_grid(plotlist=pl, nrow=2, align='v')

Neg_probe reads compared to raw_reads
# make background total neg probe count
fdata_df<-fData(Data)
negprobesnames<-rownames(fdata_df[fdata_df$Negative==TRUE,])
temp_exp<-assayDataElement(Data,elt='exprs')
negprobe_expr_fd<-temp_exp[rownames(temp_exp) %in% negprobesnames,]
tot_neg_ctrl_reads<-colSums(negprobe_expr_fd)
tot_dedup_reads<-pData(protocolData(Data))$DeduplicatedReads
df<-data.frame('aoi'= names(tot_neg_ctrl_reads),'tot_dedup_reads' = as.numeric(tot_dedup_reads),'tot_neg_ctrl_reads'=as.numeric(tot_neg_ctrl_reads))
df<-melt(df,id="aoi")
ggplot(df,aes(fill=variable,y=value,x=aoi)) +
geom_bar(position="identity",stat="identity") +
scale_y_continuous(trans = log2_trans()) +
theme(legend.position="bottom",axis.text.x = element_blank(),axis.ticks.x=element_blank())

Duplicated reads vs Background
# get dcc per plate. sum negprobe counts/dcc/plate
ggplot(pData(protocolData(Data)),
aes(x = Plate_ID, fill=Plate_ID,
y = DeduplicatedReads)) +
geom_violin() +
geom_jitter(width = .2) +
labs(y = "Deduplicated / Raw reads") +
scale_y_log10()+
geom_hline(data =pData(protocolData(Data)) ,
aes(yintercept = NTC, colour=Plate_ID)) +
theme_bw()

Duplicated reads vs ROIarea
temp_df<-cbind(pData(Data),pData(protocolData(Data)),dcc=rownames(pData(Data)))
ggplot(temp_df,
aes(x = dcc, colour=slide_name,
y = (DeduplicatedReads/area) )) +
geom_point() +
ylim(0,20) +
labs(y = "Deduplicated reads / ROI area") +
theme(axis.text.x = element_text(size =6, angle=90, hjust=1) )

Duplicated reads vs nuclei
temp_df<-cbind(pData(Data),pData(protocolData(Data)),dcc=rownames(pData(Data)))
ggplot(temp_df,
aes(x = dcc, colour=slide_name,
y = (DeduplicatedReads/nuclei) )) +
geom_point() +
ylim(0,50) + # Adjust per project
labs(y = "Deduplicated reads / nuclei") +
theme(axis.text.x = element_text(size =6, angle=90, hjust=1) )

4.3 Process Negative GeoMeans
# Calculate the negative geometric means for each module
# It will show only the negative probes geomean, so expect less segments.
negativeGeoMeans <-
esBy(negativeControlSubset(Data),
GROUP = "Module",
FUN = function(x) {
assayDataApply(x, MARGIN = 2, FUN = ngeoMean, elt = "exprs")
})
protocolData(Data)[["NegGeoMean"]] <- negativeGeoMeans
negCols <- paste0("NegGeoMean_", modules)
pData(Data)[, negCols] <- sData(Data)[["NegGeoMean"]]
for(ann in negCols) {
plt <- QC_histogram(pData(Data), ann, col_by, 2, scale_trans = "log10")
print(plt)
}

# Detatch neg_geomean columns ahead of aggregateCounts call
pData(Data) <- pData(Data)[, !colnames(pData(Data)) %in% negCols]
Show all NTC values, Freq = # of Segments with a given NTC count:
QC<-sData(Data)
ntc_df <-QC[,c("slide_name","Plate_ID","NTC_ID","NTC")]
temptable<-ntc_df %>% dplyr::count(ntc_df$slide_name,ntc_df$NTC_ID,ntc_df$Plate_ID,ntc_df$NTC)
colnames(temptable) <- c("Slide_name","NTC_ID","Plate_ID","NTC_count","Number_of_samples")
datatable(temptable, rownames = FALSE)
kable(table(NTC_Count = sData(Data)$NTC), col.names = c("NTC Count", "# of Segments"))
| 3 |
64 |
| 113 |
71 |
| 397 |
47 |
| 8704 |
94 |
kable(QC_Summary, caption = "QC Summary Table for each Segment")
QC Summary Table for each Segment
| LowReads |
272 |
4 |
| LowTrimmed |
276 |
0 |
| LowStitched |
273 |
3 |
| LowAligned |
266 |
10 |
| LowSaturation |
272 |
4 |
| LowNegatives |
276 |
0 |
| HighNTC |
276 |
0 |
| LowNuclei |
276 |
0 |
| LowArea |
265 |
11 |
| TOTAL FLAGS |
259 |
17 |
datatable(QC_Summary,
caption = "AOI QC Summary",
extensions = 'Buttons', options = list (
dom = 'Bftrip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
)
)
Show AOIs which fail critical QCs.
QC<-sData(Data)
undersat<-subset(QC, `Saturated (%)`<= QC_params$percentSaturation)
if(nrow(undersat)> 0) {
datatable(aggregate(undersat, by=list(undersat$SampleID),paste,collapse=";"),
extensions = 'Buttons', options = list (
dom = 'Bftrip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
)
)}
Subsetting our dataset has removed samples which did not pass QC
Data <- Data[, QCResults$QCStatus == "PASS"]
Generally keep the qcCutoffs parameters unchanged. Set
removeLocalOutliers to FALSE if you do not want to remove local
outliers
Data <- setBioProbeQCFlags(Data,
qcCutoffs = list(minProbeRatio = 0.1,
percentFailGrubbs = 20),
removeLocalOutliers = FALSE)
ProbeQCResults <- fData(Data)[["QCFlags"]]
Define QC table for Probe QC
qc_df <- data.frame(Passed = sum(rowSums(ProbeQCResults[, -1]) == 0),
Global = sum(ProbeQCResults$GlobalGrubbsOutlier),
Local = sum(rowSums(ProbeQCResults[, -2:-1]) > 0
& !ProbeQCResults$GlobalGrubbsOutlier))
Subset object to exclude all that did not pass Ratio & Global
testing
ProbeQCPassed <-
subset(Data,
fData(Data)[["QCFlags"]][,c("LowProbeRatio")] == FALSE &
fData(Data)[["QCFlags"]][,c("GlobalGrubbsOutlier")] == FALSE)
Data <- ProbeQCPassed
cat("After QC features:", dim(Data)[1], "\nAfter QC samples:", dim(Data)[2])
## After QC features: 18641
## After QC samples: 259
Check how many unique targets the object has
length(unique(featureData(Data)[["TargetName"]]))
## [1] 18504
Collapse to targets
target_Data <- aggregateCounts(Data)
exprs(target_Data)[1:5, 1:2]
## DSP-1001250007851-H-A02.dcc DSP-1001250007851-H-A03.dcc
## A2M 485 262
## NAT2 15 18
## ACADM 31 15
## ACADS 27 17
## ACAT1 29 24
Define LOQ SD threshold and minimum value
cutoff <- 2
minLOQ <- 2
4.4 Limit of Quantification
We define a limit of quantification (LOQ) per ROI/AOI segment based
on the negative control probes to guide the filtering of segments and
genes with low signal relative to background. The formula for
calculating the LOQ in the \(i^{th}\)
segment at \(n\) standard deviations
(\(n = 2\) for this study) is: \(LOQ_i=geomean(NegProbe_i)*geoSD(NegProbe_i)^n\)
Calculate LOQ per module tested
LOQ <- data.frame(row.names = colnames(target_Data))
for(module in modules) {
vars <- paste0(c("NegGeoMean_", "NegGeoSD_"),
module)
if(all(vars[1:2] %in% colnames(pData(target_Data)))) {
LOQ[, module] <-
pmax(minLOQ,
pData(target_Data)[, vars[1]] *
pData(target_Data)[, vars[2]] ^ cutoff)
}
}
pData(target_Data)$LOQ <- LOQ
4.5 Filtering
After determining the limit of quantification (LOQ) per segment, we
recommend filtering out either segments and/or genes with abnormally low
signal. Filtering is an important step to focus on the true biological
data of interest.
We determine the number of genes detected in each segment across the
dataset.
LOQ_Mat <- c()
for(module in modules) {
ind <- fData(target_Data)$Module == module
Mat_i <- t(esApply(target_Data[ind, ], MARGIN = 1,
FUN = function(x) {
x > LOQ[, module]
}))
LOQ_Mat <- rbind(LOQ_Mat, Mat_i)
}
# ensure ordering since this is stored outside of the geomxSet
LOQ_Mat <- LOQ_Mat[fData(target_Data)$TargetName, ]
4.5.1 Segment Gene Detection
We first filter out segments with exceptionally low signal. These
segments will have a small fraction of panel genes detected above the
LOQ relative to the other segments in the study. Let’s visualize the
distribution of segments with respect to their % genes detected:
Save detection rate information to pheno data
pData(target_Data)$GenesDetected <-
colSums(LOQ_Mat, na.rm = TRUE)
pData(target_Data)$GeneDetectionRate <-
pData(target_Data)$GenesDetected / nrow(target_Data)
Determine detection thresholds: 1%, 5%, 10%, 15%, >15%
pData(target_Data)$DetectionThreshold <-
cut(pData(target_Data)$GeneDetectionRate,
breaks = c(0, 0.01, 0.05, 0.1, 0.15, 0.2,1),
labels = c("<1%", "1-5%", "5-10%", "10-15%", "15-20%", ">20%"))
# stacked bar plot of different cut points (1%, 5%, 10%, 15%)
ggplot(pData(target_Data),
aes(x = DetectionThreshold)) +
geom_bar(aes(fill = ANN2)) +
geom_text(stat = "count", aes(label = ..count..), vjust = -0.5) +
theme_bw() +
scale_y_continuous(expand = expansion(mult = c(0, 0.1))) +
scale_fill_manual(values=color_list$ANN2) +
labs(x = "Gene Detection Rate",
y = "Segments, #",
fill = "Segment Type")

cut percent genes detected at 1, 5, 10, 15
kable(table(pData(target_Data)$DetectionThreshold,
pData(target_Data)$ANN2))
| <1% |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
| 1-5% |
0 |
1 |
0 |
0 |
6 |
0 |
0 |
| 5-10% |
8 |
3 |
0 |
1 |
5 |
1 |
0 |
| 10-15% |
13 |
11 |
1 |
3 |
2 |
3 |
0 |
| 15-20% |
7 |
5 |
3 |
7 |
5 |
10 |
0 |
| >20% |
5 |
11 |
55 |
13 |
15 |
41 |
23 |
# set threshold for detectionlevel
# default 0.1
gene_det_threshold <- 0.05
target_Data <-
target_Data[, pData(target_Data)$GeneDetectionRate >= gene_det_threshold]
dim(target_Data)
## Features Samples
## 18504 251
4.6 Manual removal of samples/classes
active_aois<-names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))
re-Collect annotations
# gather the data and plot in order: class, slide name, region, segment
#count_mat <- dplyr::count(pData(Data), ANN1,ANN2,ANN3,ANN4,slide_name)
temp_qc <- temp_df
temp_qc$QCResult <- QCResults$QCStatus
temp_qc$QCResult[temp_qc$QCResult == "WARNING"] <- "X"
#count_mat <- dplyr::count(a, ANN1, ANN2, slide_name, QCResult)
count_mat <- temp_qc %>% dplyr::count(temp_qc[c(ann_names, "QCResult")])
test_gr <- gather_set_data(count_mat, 1:(ann_size+1))
test_gr$x <- factor(test_gr$x, labels = c(ann_names, "QCResult"))
re-Plot Sankey
ggplot(test_gr, aes(x, id = id, split = y, value = n)) +
geom_parallel_sets(aes(fill = ANN2), alpha = 0.5, axis.width = 0.1) +
geom_parallel_sets_axes(axis.width = 0.2) +
geom_parallel_sets_labels(color = "white", size = 5) +
theme_classic(base_size = 17) +
theme(legend.position = "bottom",
axis.ticks.y = element_blank(),
axis.line = element_blank(),
axis.text.y = element_blank()) +
scale_y_continuous(expand = expansion(0)) +
scale_x_discrete(expand = expansion(0)) +
scale_fill_manual(values=color_list$ANN2) +
labs(x = "", y = "") +
annotate(geom = "segment", x = 3.25, xend = 3.25, y = 10,
yend = 60, lwd = 2) +
annotate(geom = "text", x = 3.19, y = 25, angle = 90, size = 5,
hjust = 0.5, label = "50 segments")

4.7 Gene Detection Rate
Calculate detection rate
LOQ_Mat <- LOQ_Mat[, colnames(target_Data)]
fData(target_Data)$DetectedSegments <- rowSums(LOQ_Mat, na.rm = TRUE)
fData(target_Data)$DetectionRate <-
fData(target_Data)$DetectedSegments / nrow(pData(target_Data))
Gene of interest detection table
goi <- c("PDCD1", "CD274", "IFNG", "CD8A", "CD68", "EPCAM",
"KRT18", "NPHS1", "NPHS2", "CALB1", "CLDN8")
goi_df <- data.frame(
Gene = goi,
Number = fData(target_Data)[goi, "DetectedSegments"],
DetectionRate = percent(fData(target_Data)[goi, "DetectionRate"]))
4.8 Gene Filtering
We will graph the total number of genes detected in different
percentages of segments. Based on the visualization below, we can better
understand global gene detection in our study and select how many low
detected genes to filter out of the dataset. Gene filtering increases
performance of downstream statistical tests and improves interpretation
of true biological signal.
Plot detection rate
plot_detect <- data.frame(Freq = c(1, 5, 10, 20, 30, 50))
plot_detect$Number <-
unlist(lapply(c(0.01, 0.05, 0.1, 0.2, 0.3, 0.5),
function(x) {sum(fData(target_Data)$DetectionRate >= x)}))
plot_detect$Rate <- plot_detect$Number / nrow(fData(target_Data))
rownames(plot_detect) <- plot_detect$Freq
ggplot(plot_detect, aes(x = as.factor(Freq), y = Rate, fill = Rate)) +
geom_bar(stat = "identity") +
geom_text(aes(label = formatC(Number, format = "d", big.mark = ",")),
vjust = 1.6, color = "black", size = 4) +
scale_fill_gradient2(low = "orange2", mid = "lightblue",
high = "dodgerblue3", midpoint = 0.65,
limits = c(0,1),
labels = scales::percent) +
theme_bw() +
scale_y_continuous(labels = scales::percent, limits = c(0,1),
expand = expansion(mult = c(0, 0))) +
labs(x = "% of Segments",
y = "Genes Detected, % of Panel > LOQ")

Subset to target genes detected in at least 10% of the samples. Also
manually include the negative control probe, for downstream use
# default=0.1
negativeProbefData <- subset(fData(target_Data), CodeClass == "Negative")
neg_probes <- unique(negativeProbefData$TargetName)
target_Data <-
target_Data[fData(target_Data)$DetectionRate >= 0.05 |
fData(target_Data)$TargetName %in% neg_probes, ]
# retain only detected genes of interest
goi <- goi[goi %in% rownames(target_Data)]
dim(target_Data)
## Features Samples
## 12241 251
5 Normalization
We will now normalize the GeoMx data for downstream visualizations
and differential expression. The two common methods for normalization of
DSP-NGS RNA data are i) quartile 3 (Q3) or ii) background
normalization.
Both of these normalization methods estimate a normalization factor
per segment to bring the segment data distributions together. More
advanced methods for normalization and modeling are under active
development. However, for most studies, these methods are sufficient for
understanding differences between biological classes of segments and
samples.
Q3 normalization is typically the preferred normalization strategy
for most DSP-NGS RNA studies. Given the low negative probe counts in
this particular dataset as shown during Segment QC, we would further
avoid background normalization as it may be less stable.
Before normalization, we will explore the relationship between the
upper quartile (Q3) of the counts in each segment with the geometric
mean of the negative control probes in the data. Ideally, there should
be a separation between these two values to ensure we have stable
measure of Q3 signal. If you do not see sufficient separation between
these values, you may consider more aggressive filtering of low signal
segments/genes.
Graph Q3 value vs negGeoMean of Negatives
ann_of_interest <- "ANN2"
Stat_data <-
data.frame(row.names = colnames(exprs(target_Data)),
Segment = colnames(exprs(target_Data)),
Annotation = pData(target_Data)[, ann_of_interest],
Q3 = unlist(apply(exprs(target_Data), 2,
quantile, 0.75, na.rm = TRUE)),
NegProbe = exprs(target_Data)[neg_probes, ])
Stat_data_m <- melt(Stat_data, measure.vars = c("Q3", "NegProbe"),
variable.name = "Statistic", value.name = "Value")
plt1 <- ggplot(Stat_data_m,
aes(x = Value, fill = Statistic)) +
geom_histogram(bins = 40) + theme_bw() +
scale_x_continuous(trans = "log2") +
facet_wrap(~Annotation, nrow = 1) +
scale_fill_brewer(palette = 3, type = "qual") +
labs(x = "Counts", y = "Segments, #")
plt2 <- ggplot(Stat_data,
aes(x = NegProbe, y = Q3, color = Annotation)) +
geom_abline(intercept = 0, slope = 1, lty = "dashed", color = "darkgray") +
geom_point() + guides(color = "none") + theme_bw() +
scale_x_continuous(trans = "log2") +
scale_y_continuous(trans = "log2") +
theme(aspect.ratio = 1) +
labs(x = "Negative Probe GeoMean, Counts", y = "Q3 Value, Counts")
plt3 <- ggplot(Stat_data,
aes(x = NegProbe, y = Q3 / NegProbe, color = Annotation)) +
geom_hline(yintercept = 1, lty = "dashed", color = "darkgray") +
geom_point() + theme_bw() +
scale_x_continuous(trans = "log2") +
scale_y_continuous(trans = "log2") +
theme(aspect.ratio = 1) +
labs(x = "Negative Probe GeoMean, Counts", y = "Q3/NegProbe Value, Counts")
btm_row <- plot_grid(plt2, plt3, nrow = 1, labels = c("B", ""),
rel_widths = c(0.43,0.57))
plot_grid(plt1, btm_row, ncol = 1, labels = c("A", ""))

Q3 norm (75th percentile) for WTA/CTA with or without custom
spike-ins
target_Data <- normalize(target_Data ,
norm_method = "quant",
desiredQuantile = .75,
toElt = "q_norm")
#, data_type = "RNA" depricated after 4.1
Quantile Normalization
quantile <- normalize.quantiles(target_Data@assayData[["exprs"]])
rownames(quantile) <- rownames(target_Data@assayData[["exprs"]])
colnames(quantile) <- colnames(target_Data@assayData[["exprs"]])
assayDataElement(object = target_Data, elt = "quantile_norm") <- quantile
Background normalization for WTA/CTA without custom spike-in
target_Data <- normalize(target_Data,
norm_method = "neg",
fromElt = "exprs",
toElt = "neg_norm")
# , data_type = "RNA" depricated after 4.1
5.1 Visualize the first 10 segments with
each normalization method
Use the tab-menu to navigate!
#Fix zero values, which go to -inf in log transform in standard boxplot
# temp <-as.matrix(exprs((target_Data)[,1:10]))
# long <- melt(temp)
# colnames(long) <- c("gene","segment","count")
# ggplot(long, aes(x=segment,y=count)) +
# geom_boxplot(fill="#9EDAE5") +
# scale_y_continuous(trans=scales::pseudo_log_trans(base = 10)) +
# scale_x_discrete(labels=c(1:10)) +
# labs(title="Raw counts", x="segment", y = "Counts, Raw")
#
#
# temp <-as.matrix(assayDataElement(target_Data[,1:10], elt = "q_norm"))
# long <- melt(temp)
# colnames(long) <- c("gene","segment","count")
# ggplot(long, aes(x=segment,y=count)) +
# geom_boxplot(fill = "#2CA02C") +
# scale_y_continuous(trans=scales::pseudo_log_trans(base = 10)) +
# scale_x_discrete(labels=c(1:10)) +
# labs(title="Q3 Norm Counts", x="segment", y = "Counts, Q3 Normalized")
#
#
# temp <-as.matrix(assayDataElement(target_Data[,1:10], elt = "neg_norm"))
# long <- melt(temp)
# colnames(long) <- c("gene","segment","count")
# ggplot(long, aes(x=segment,y=count)) +
# geom_boxplot(fill = "#FF7F0E") +
# scale_y_continuous(trans=scales::pseudo_log_trans(base = 10)) +
# scale_x_discrete(labels=c(1:10)) +
# labs(title="Neg Norm Counts", x="segment", y = "Counts, Neg. Normalized")
raw counts
boxplot(exprs(target_Data)[,1:8],
col = "#9EDAE5", main = "Raw Counts",
log = "y", names = 1:8, xlab = "Segment",
ylab = "Counts, Raw")

Q3 normalized
boxplot(assayDataElement(target_Data[,1:8], elt = "q_norm"),
col = "#2CA02C", main = "Q3 Norm Counts",
log = "y", names = 1:8, xlab = "Segment",
ylab = "Counts, Q3 Normalized")

Quantile normalized (testing phase)
boxplot(assayDataElement(target_Data[,1:8], elt = "quantile_norm"),
col = "pink", main = "Quantile Norm Counts",
log = "y", names = 1:8, xlab = "Segment",
ylab = "Counts, Quantile Normalized")

Negative probe normalization
boxplot(assayDataElement(target_Data[,1:8], elt = "neg_norm"),
col = "#FF7F0E", main = "Neg Norm Counts",
log = "y", names = 1:8, xlab = "Segment",
ylab = "Counts, Neg. Normalized")

6 Unsupervised Analysis
6.1 UMAP
Use the tab-menu to navigate!
1
custom_umap <- umap::umap.defaults
custom_umap$random_state <- 42
# run UMAP
umap_out <-
umap(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
config = custom_umap)
pData(target_Data)[, c("UMAP1", "UMAP2")] <- umap_out$layout[, c(1,2)]
ggplot(pData(target_Data),
aes(x = UMAP1, y = UMAP2, color = slide_name, shape = ANN1)) +
geom_point(size = 3) +
#geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
theme_bw()

batch_correction_needed = FALSE
2
ggplot(pData(target_Data),
aes(x = UMAP1, y = UMAP2, color = ANN3, shape = ANN1)) +
geom_point(size = 3) +
#geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
theme_bw()

6.2 Batch correction
If the data is observed to have a batch effect it can be corrected
with the methods: RUV4, LIMMA, or Combat-Seq. Each method is done and
the best one is picked and used.
The standR package is short for Spatial transcriptomics analyzes and
decoding in R, it aims at providing good practice pipeline and useful
functions for users to analyze Nanostring’s GeoMx DSP data. In the
Nanostring’s GeoMX DSP protocol, due to the fact that one slide is only
big enough for a handful of tissue segments (ROIs), it is common that we
see the DSP data being confounded by the batch effect introduced by
different slides. In order to establish fair comparison between ROIs
later on, it is necessary to remove this batch effect from the data. (https://bioconductor.org/packages/release/bioc/vignettes/standR/inst/doc/standR_introduction.html)
For RUV4 correction, the function is requiring 3 parameters other
than the input object, including factors: the factor of interest,
i.e. the biological variation we plan to keep; NCGs: the list of
negative control genes detected using the function findNCGs; and k: is
the number of unwanted factors to use, in the RUV documentation, it is
suggest that we should use the smallest k once we don’t observe
technical variation in the data.
Another option is set the parameter method to “Limma”, which uses the
remove batch correction method from limma. In this mode, the function is
requiring 2 parameters, including batch: a vector that indicating
batches for all samples; and design: a design matrix which is generated
by model.matrix, in the design matrix, all biologically-relevant factors
should be included.
ComBat-seq is a batch effect adjustment tool for bulk RNA-seq count
data. It is an improved model based on the popular ComBat, to address
its limitations through novel methods designed specifically for RNA-Seq
studies. ComBat-seq takes untransformed, raw count matrix as input. Same
as ComBat, it requires a known batch variable. (https://github.com/zhangyuqing/ComBat-seq)
Set up standR object
count_geomx <- as.data.frame(target_Data@assayData[["exprs"]])
sample_geomx <- target_Data@phenoData@data
sample_geomx$roi <- target_Data@protocolData@data$roi
sample_geomx <- as.data.frame(sample_geomx)
sample_geomx$Sample_ID <- rownames(sample_geomx)
sample_geomx$SegmentDisplayName <- paste(sample_geomx$`scan name`, sample_geomx$roi, sample_geomx$segment, sep = " | ")
sample_geomx$ROICoordinateX <- 1
sample_geomx$ROICoordinateY <- 1
feature_geomx <- fData(Data)
feature_geomx <- feature_geomx[c("RTS_ID", "TargetName", "ProbeID", "Negative")]
feature_geomx <- as.data.frame(feature_geomx)
rownames(feature_geomx) <- NULL
matching <- sample_geomx$SegmentDisplayName[sample_geomx$Sample_ID %in% colnames(count_geomx)]
colnames(count_geomx) <- matching
count_geomx$TargetName <- rownames(count_geomx)
rownames(count_geomx) <- NULL
spe <- readGeoMx(count_geomx, sample_geomx, featureAnnoFile = feature_geomx, hasNegProbe = TRUE)
colData(spe)$regions <- paste0(colData(spe)$ANN2,"_",colData(spe)$ANN1) |>
(\(.) gsub("_Geometric Segment","",.))() |>
paste0("_",colData(spe)$pathology) |>
(\(.) gsub("_NA","_ns",.))()
colData(spe)$regions <- paste0(colData(spe)$ANN2,"_",colData(spe)$ANN1) |>
(\(.) gsub("_Geometric Segment","",.))() |>
paste0("_",colData(spe)$pathology) |>
(\(.) gsub("_NA","_ns",.))()
colData(spe)$biology <- paste0(colData(spe)$regions)
See optimal k value for RUV4
spe <- findNCGs(spe, batch_name = "slide_name", top_n = 500)
findBestK(spe, maxK = 10, factor_of_int = "biology", NCGs = metadata(spe)$NCGs, factor_batch = "slide_name")
RUV4
ruv4 <- geomxBatchCorrection(spe, factors = "biology",
NCGs = metadata(spe)$NCGs, k = 1)
plotPairPCA(ruv4, assay = 2, color = slide_name, shape = regions, title = "RUV4 removeBatch")
plotRLExpr(ruv4, assay = 2, color = `slide_name`) + ggtitle("RUV4 removeBatch")
Limma
limma <- geomxBatchCorrection(spe,
batch = colData(spe)$`slide_name`, method = "Limma",
design = model.matrix(~ 0 + ANN1 + regions,
data = colData(spe)))
plotPairPCA(limma, assay = 2, color = slide_name, shape = regions, title = "Limma removeBatch")
plotRLExpr(limma, assay = 2, color = slide_name) + ggtitle("Limma removeBatch")
CombatSeq
adjusted <- ComBat_seq(target_Data@assayData[["exprs"]], batch=target_Data@phenoData@data[["slide_name"]], group=target_Data@phenoData@data[["ANN2"]])
assayDataElement(object = target_Data, elt = "combat") <- adjusted
umap_out2 <-
umap(t(log2(assayDataElement(target_Data , elt = "combat"))),
config = custom_umap)
pData(target_Data)[, c("UMAP1", "UMAP2")] <- umap_out2$layout[, c(1,2)]
ggplot(pData(target_Data),
aes(x = UMAP1, y = UMAP2, color = slide_name, shape = ANN1)) +
geom_point(size = 3) +
#geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
theme_bw() + ggtitle("CombatSeq removeBatch")
Compare limma vs RUV4
spe_list <- list(spe, ruv4, limma)
plotClusterEvalStats(spe_list = spe_list,
bio_feature_name = "regions",
batch_feature_name = "slide_name",
data_names = c("Raw","RUV4","Limma"))
Add batch correction result to target_Data
neg_probes_save <- t(as.matrix(target_Data@assayData$q_norm["NegProbe-WTX",]))
rownames(neg_probes_save) <- "NegProbe-WTX"
# Depending on correct method, change the word "limma" to "ruv4" or vice versa.
limma <-limma@assays@data@listData[["logcounts"]]
limma <- rbind(limma, neg_probes_save)
colnames(limma) <- colnames(as.data.frame(target_Data@assayData[["q_norm"]]))
assayDataElement(object = target_Data, elt = "limma") <- limma
ruv4 <-ruv4@assays@data@listData[["logcounts"]]
ruv4 <- rbind(ruv4, neg_probes_save)
colnames(ruv4) <- colnames(as.data.frame(target_Data@assayData[["q_norm"]]))
assayDataElement(object = target_Data, elt = "ruv4") <- ruv4
Choose method
# choose method to replace q_norm or set it to ""
method <- ""
# replace q_norm with chosen method
if (!method == "") {
# save orginal q_norm
assayDataElement(object = target_Data, elt = "original") <- assayDataElement(object = target_Data, elt = "q_norm")
assayDataElement(object = target_Data, elt = "q_norm") <- assayDataElement(object = target_Data, elt = method)
print(paste("Batch correction method:", method))
} else {print("No batch correction needed")}
## [1] "No batch correction needed"
Umap after batch correction
umap_out <-
umap(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
config = custom_umap)
pData(target_Data)[, c("UMAP1", "UMAP2")] <- umap_out$layout[, c(1,2)]
ggplot(pData(target_Data),
aes(x = UMAP1, y = UMAP2, color = slide_name, shape = ANN1)) +
geom_point(size = 3) +
#geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
theme_bw()
6.3 Run tSNE
Use the tab-menu to navigate!
One common approach to understanding high-plex data is dimension
reduction. Two common methods are UMAP and tSNE, which are
non-orthogonally constrained projections that cluster samples based on
overall gene expression. In this study, we see by either UMAP (from the
umap package) or tSNE (from the Rtsne package)
1
tsne_out <-
Rtsne(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
perplexity = ncol(target_Data)*.15)
pData(target_Data)[, c("tSNE1", "tSNE2")] <- tsne_out$Y[, c(1,2)]
ggplot(pData(target_Data),
aes(x = tSNE1, y = tSNE2, shape = slide_name, color = ANN2)) +
geom_point(size = 3) +
#geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
theme_bw()

2
tsne_out <-
Rtsne(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
perplexity = ncol(target_Data)*.15)
pData(target_Data)[, c("tSNE1", "tSNE2")] <- tsne_out$Y[, c(1,2)]
ggplot(pData(target_Data),
aes(x = tSNE1, y = tSNE2, color = ANN3, shape = ANN1)) +
geom_point(size = 3) +
#geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
theme_bw()

6.4 Clustering high CV Genes
Another approach to explore the data is to calculate the coefficient
of variation (\(CV\)) for each gene
(\(g\)) using the formula \(CV_g=SD_g/mean_g\). We then identify genes
with high CVs that should have large differences across the various
profiled segments. This unbiased approach can reveal highly variable
genes across the study.
We plot the results using unsupervised hierarchical clustering,
displayed as a heatmap.
# create a log2 transform of the data for analysis
assayDataElement(object = target_Data, elt = "log_q") <-
assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")
# create CV function
calc_CV <- function(x) {sd(x) / mean(x)}
CV_dat <- assayDataApply(target_Data,
elt = "log_q", MARGIN = 1, calc_CV)
# show the highest CD genes and their CV values
sort(CV_dat, decreasing = TRUE)[1:5]
## CAMK2N1 AKR1C1 AQP2 REN GDF15
## 0.6344192 0.5238995 0.4752667 0.4414027 0.4276390
Table of CV values
# show the highest CD genes and their CV values
datatable(as.data.frame(CV_dat),
extensions = 'Buttons', options = list (
order = list(1, 'desc'),
dom = 'Bftrip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
), caption = "CV values of genes"
) %>% formatRound(columns=c("CV_dat"), digits=3)
Heatmap genes in the top 3rd of the CV values
GOI <- names(CV_dat)[CV_dat > quantile(CV_dat, 0.75)]
pheatmap(assayDataElement(target_Data[GOI, ], elt = "log_q"),
scale = "row",
cutree_cols = 3,
cutree_rows = 2,
show_rownames = FALSE, show_colnames = TRUE,
border_color = NA,
drop_levels = TRUE,
clustering_method = "average",
clustering_distance_rows = "correlation",
clustering_distance_cols = "correlation",
breaks = seq(-3, 3, 0.05),
color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
annotation_colors = color_list,
annotation_col = pData(target_Data)[, ann_names])

assayDataElement(object = target_Data, elt = "log_q") <- assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")
log_q <-as.data.frame(assayDataElement(target_Data, elt= "log_q"))
#batch <-as.data.frame(assayDataElement(target_Data, elt= "batch"))
6.5.0 Create subset of data
# determine AOIs to use
#active_aois<-rownames(ann)[ann$patient=="p4"]
active_aois<- names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))
6.5.1 Clustering high CV genes for
subset
Calculating CV values
# create a log2 transform of the data for analysis
assayDataElement(object = target_Data, elt = "log_q") <-
assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")
# create CV function
calc_CV <- function(x) {sd(x) / mean(x)}
CV_dat <- assayDataApply(target_Data[,active_aois],
elt = "log_q", MARGIN = 1, calc_CV)
Table of CV values
# show the highest CD genes and their CV values
datatable(as.data.frame(CV_dat),
extensions = 'Buttons', options = list (
order = list(1, 'desc'),
dom = 'Bftrip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
), caption = "CV values of genes"
) %>% formatRound(columns=c("CV_dat"), digits=3)
Heatmap on of subset, genes in the top 3rd of the CV
values
# Identify genes in the top 3rd of the CV values
GOI <- names(CV_dat)[CV_dat > quantile(CV_dat, 0.75)]
pheatmap(assayDataElement(target_Data[GOI,active_aois ], elt = "log_q"),
scale = "row",
fontsize_row = 5,
cutree_cols = 3,
cutree_rows = 2,
show_rownames = FALSE, show_colnames = TRUE,
border_color = NA,
clustering_method = "average",
clustering_distance_rows = "correlation",
clustering_distance_cols = "correlation",
breaks = seq(-3, 3, 0.05),
color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
annotation_colors = color_list,
annotation_col =
pData(target_Data)[, ann_names])

7.1 Differential Expression

t-test
#gc()
plots<-list()
tables<-list()
labels<-list()
test<-"ttest"
mtc<-"BY"
#options: "holm" "hochberg" "hommel" "bonferroni" "BH" "BY" "fdr"
counter=1
comps_df<-data.frame(comp='',val='')
for(region in unique(pData(target_Data)$ANN3)) {
for (active_group1 in unique(pData(target_Data)$ANN1)) {
for (active_group2 in unique(pData(target_Data)$ANN1)) {
#supress reduncant compares
if(active_group1==active_group2) {next}
comp<-paste(sort(c(region, active_group1,active_group2)),collapse = "_")
if(comp %in% comps_df$comp) {next}
temp_df<-data.frame(comp=comp ,val=1)
comps_df<-rbind(comps_df,temp_df)
labels[[counter]]<-paste(active_group1," vs ", active_group2)
group1<-log_q[,names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))[pData(target_Data)$ANN1==active_group1 & pData(target_Data)$ANN3==region]]
group2<-log_q[,names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))[pData(target_Data)$ANN1==active_group2 & pData(target_Data)$ANN3==region]]
#run t_tests
results<-as.data.frame ( apply(log_q, 1, function(x) t.test(x[colnames(group1)],x[colnames(group2)])$p.value) )
colnames(results)<-"raw_p_value"
#multiple_testing_correction
adj_p_value<- p.adjust(results$raw_p_value,method=mtc)
results<-cbind(results,adj_p_value)
#calc_fdr
FDR<- p.adjust(results$raw_p_value,method="fdr")
results<-cbind(results,FDR)
#fold_changes
#as base data is already log transformed, means need to be subtracted to get FC in log space
fchanges<-as.data.frame(apply(log_q, 1, function(x) (mean(x[colnames(group1)]) - mean(x[colnames(group2)]))))
colnames(fchanges)<-"FC"
#paste("FC",active_group1," / ",active_group2)
results<-cbind(results,fchanges)
#add genenames
results$Gene<-rownames(results)
#set categories based on P-value & FDR for plotting
results$Color <- "NS or FC < 1"
results$Color[results$raw_p_value < 0.05] <- "P < 0.05"
results$Color[results$FDR < 0.05] <- "FDR < 0.05"
results$Color[results$FDR < 0.001] <- "FDR < 0.001"
results$Color[abs(results$FC) <1] <- "NS or FC < 1"
results$Color <- factor(results$Color,
levels = c("NS or FC < 1", "P < 0.05", "FDR < 0.05", "FDR < 0.001"))
#vulcanoplot
# pick top genes for either side of volcano to label
# order genes for convenience:
results$invert_P <- (-log10(results$adj_p_value)) * sign(results$FC)
top_g <- c()
top_g <- c(top_g,
results[, 'Gene'][
order(results[, 'invert_P'], decreasing = TRUE)[1:20]],
results[, 'Gene'][order(results[, 'invert_P'], decreasing = FALSE)[1:20]])
top_g<- unique(top_g)
results <- results[, -1*ncol(results)] # remove invert_P from matrix
# Graph results
plots[[counter]]<- ggplot(results,
aes(x = FC, y = -log10(raw_p_value),
color = Color, label = Gene)) +
geom_vline(xintercept = c(1, -1), lty = "dashed") +
geom_hline(yintercept = -log10(0.05), lty = "dashed") +
geom_point() +
labs(x = paste("Enriched in", active_group2," <- log2(FC) -> Enriched in", active_group1),
y = "Significance, -log10(P)",
color = "Significance") +
scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
`FDR < 0.05` = "lightblue",
`P < 0.05` = "orange2",
`NS or FC < 0.5` = "gray"),
guide = guide_legend(override.aes = list(size = 4))) +
scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
geom_text_repel(data = subset(results, (-1>FC| FC>1) & FDR < 0.05 & Gene %in% top_g),
point.padding = 0.15, color = "black", size=3.5,
min.segment.length = .1, box.padding = .2, lwd = 2,
max.overlaps = 50) +
theme_bw(base_size = 10) +
theme(legend.position = "bottom") +
ggtitle(paste("class: ", region," - ", test, mtc,"multitest corr"))
#store tables for display later
tables[[counter]]<-results
counter = counter+1
#datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
}
}
}
grid.arrange(grobs=plots,ncol=2)

#strangly does not appear in html output -> chucks are limited to one datatable per chuck. The htmltools are a way around this but does show them stacked in a box.
results_tables <- htmltools::tagList()
for (c in (2:counter-1)) {
#Gene %in% GOI
results_tables[[c]] <- datatable(subset(tables[[c]], Color == c("FDR < 0.001", "P < 0.05")),
rownames=FALSE,
extensions = c('Buttons'), options = list (
dom = 'Bftrip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
), height = 550,
caption = paste("DE results ", labels[[c]]),filter='top') %>% formatRound(columns=c("raw_p_value","adj_p_value","FDR","FC"), digits=3)
#cat('\n\n<!-- -->\n\n')
}
results_tables
print("") # for layout of next section
## [1] ""
7.2 DE analysis with LMM
A common statistical approach is to use a linear mixed-effect model
(LMM). The LMM allows the user to account for the subsampling per
tissue; in other words, we adjust for the fact that the multiple regions
of interest placed per tissue section are not independent observations,
as is the assumption with other traditional statistical tests. The
formulation of the LMM model depends on the scientific question being
asked.
Overall, there are two flavors of the LMM model when used with GeoMx
data: i) with and ii) without random slope.
When comparing features that co-exist in a given tissue section, a
random slope is included in the LMM model. When comparing features that
are mutually exclusive in a given tissue section the LMM model does not
require a random slope.
Mostly, we use patient/sample as Random Intercept when they are
combined on slides.

# convert test variables to factors
pData(target_Data)$testClass <- factor(pData(target_Data)$ANN1, unique(target_Data$ANN1))
pData(target_Data)[["slide"]] <- factor(pData(target_Data)[["slide_name"]])
#assayDataElement(object = target_Data, elt = "log_q") <- assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")
# run LMM:
# formula follows conventions defined by the lme4 package
lmm_results <- c()
for (region in unique(pData(target_Data)$ANN3)) {
ind <- pData(target_Data)$ANN3 == region
mixedOutmc <-
mixedModelDE(target_Data[,ind],
elt = "log_q",
modelFormula = ~ testClass + (1 | slide),
groupVar = "testClass",
nCores = parallel::detectCores(),
multiCore = TRUE)
# format results as data.frame
r_test <- do.call(rbind, mixedOutmc["lsmeans", ])
tests <- rownames(r_test)
r_test <- as.data.frame(r_test)
r_test$Contrast <- tests
# use lapply in case you have multiple levels of your test factor to
# correctly associate gene name with it's row in the results table
r_test$Gene <-
unlist(lapply(colnames(mixedOutmc),
rep, nrow(mixedOutmc["lsmeans", ][[1]])))
r_test$Subset <- region
r_test$FDR <- p.adjust(r_test$`Pr(>|t|)`, method = "fdr")
r_test <- r_test[, c("Gene", "Subset", "Contrast", "Estimate",
"Pr(>|t|)", "FDR")]
lmm_results <- rbind(lmm_results, r_test)
}
7.3 Vulcanoplot + table of LMM
# Categorize Results based on P-value & FDR for plotting
fc_threshold = 1
lmm_results$Color <- paste("NS or FC < ",fc_threshold = 1)
lmm_results$Color[lmm_results$`Pr(>|t|)` < 0.05] <- "P < 0.05"
lmm_results$Color[lmm_results$FDR < 0.05] <- "FDR < 0.05"
lmm_results$Color[lmm_results$FDR < 0.001] <- "FDR < 0.001"
lmm_results$Color[abs(lmm_results$Estimate) < fc_threshold] <- paste("NS or FC < ",fc_threshold = 1)
lmm_results$Color <- factor(lmm_results$Color,
levels = c("NS or FC < 1", "P < 0.05",
"FDR < 0.05", "FDR < 0.001"))
counter = 1
plots_lmm <- list()
for(c in unique(lmm_results$Subset) ) {
lmm_results_slice = lmm_results[lmm_results$Subset == c,]
# pick top genes for either side of volcano to label
# order genes for convenience:
lmm_results_slice$invert_P <- (-log10(lmm_results_slice$`Pr(>|t|)`)) * sign(lmm_results_slice$Estimate)
#loop here over tested conditions if applicable
top_g <- c()
top_g <- c(top_g,
lmm_results_slice[, 'Gene'][
order(lmm_results_slice[, 'invert_P'], decreasing = TRUE)[1:15]],
lmm_results_slice[, 'Gene'][
order(lmm_results_slice[, 'invert_P'], decreasing = FALSE)[1:15]])
top_g <- unique(top_g)
lmm_results_slice <- lmm_results_slice[, -1*ncol(lmm_results_slice)] # remove invert_P from matrix
# Flip Contrast
contrast_lab <- as.character(lmm_results_slice$Contrast)
contrast_lab <- strsplit(contrast_lab, "-")[[1]]
contrast_lab <- paste(contrast_lab[2], "-", contrast_lab[1])
# Graph results
plots_lmm[[counter]] <- ggplot(lmm_results_slice,
aes(x = Estimate, y = -log10(`Pr(>|t|)`),
color = Color, label = Gene)) +
geom_vline(xintercept = c(fc_threshold, -fc_threshold), lty = "dashed") +
geom_hline(yintercept = -log10(0.05), lty = "dashed") +
geom_point() +
labs(
x = paste(contrast_lab, " log2(FC)"),
y = "Significance, -log10(P)",
color = "Significance") +
scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
`FDR < 0.05` = "lightblue",
`P < 0.05` = "orange2",
`NS or FC < 1` = "gray"),
guide = guide_legend(override.aes = list(size = 4))) +
scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
geom_text_repel(data = subset(lmm_results_slice, `Pr(>|t|)` < 0.001 & abs(lmm_results_slice$Estimate) > fc_threshold & Gene %in% top_g),
point.padding = 0.15, color = "black",size=5,
min.segment.length = .1, box.padding = .2, lwd = 2,
max.overlaps = 50) +
theme_bw(base_size = 16) +
theme(legend.position = "bottom") +
facet_wrap(~Subset, scales = "free_y")
counter <- counter + 1
}
grid.arrange(grobs=plots_lmm,ncol=2)

#subset(lmm_results, Gene %in% GOI)
datatable(lmm_results, rownames = FALSE,
extensions = 'Buttons', options = list (
dom = 'Bftrip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
),
caption = "DE results for Genes of Interest (>75% CV)",filter='top') %>% formatRound(columns=c("Estimate","Pr(>|t|)","FDR"), digits=3)
7.4 Plotting Genes of Interest
my_gois <-unique(subset(lmm_results, `FDR` < 0.001)$Gene)
tmp_tbl<-subset(lmm_results, Gene %in% my_gois)
my_gois <- c()
for (contrast in unique(tmp_tbl$Contrast)) { # gene of interest for all contrasts
#for (contrast in c("Jux_Glo - Pro_Tub")) {
ind <- tmp_tbl$Contrast == contrast
goi <- tmp_tbl[ind, "Gene"][order(tmp_tbl[ind, "Estimate"], decreasing = FALSE)[1:3]]
my_gois <- c(my_gois, goi)
goi <- tmp_tbl[ind, "Gene"][order(tmp_tbl[ind, "Estimate"], decreasing = TRUE)[1:3]]
my_gois <- c(my_gois, goi)
}
if(nrow(tmp_tbl) > 1) {
datatable(tmp_tbl,rownames = FALSE,caption = "DE results for Genes of Interest ",filter='top') %>% formatRound(columns=c("Estimate","Pr(>|t|)","FDR"), digits=3)
for (my_goi in my_gois) {
# show expression for a single target
a<-ggplot(pData(target_Data),
aes(x = ANN2, fill = ANN2,
y = as.numeric(assayDataElement(target_Data[my_goi, ], elt = "q_norm")))) +
geom_violin() +
geom_jitter(width = .2) +
labs(y = paste(my_goi,"Expression")) +
scale_y_continuous(trans = "log2") +
#facet_wrap(~ANN3, nrow=1) + theme_bw(base_size = 16) +
theme_bw() + coord_flip()
print(a)
}
}else{
print("No significant lMM results to plot")
}






7.5 Heatmap of Significant Genes
In addition to generating individual gene box plots or volcano plots,
we can again create a heatmap from our data. This time rather than
utilizing CV to select genes, we can use the P-value or FDR values to
select genes. Here, we plot all genes with an FDR < 0.001.
my_gois <-unique(subset(lmm_results, `FDR` < 0.001)$Gene) # 100 to prevent long runtime
if(length(my_gois)<2) {
print("No significant results to show")
}else{
pheatmap(log2(assayDataElement(target_Data[my_gois, ], elt = "q_norm")),
scale = "row",
show_rownames = TRUE, show_colnames = TRUE,
border_color = NA,
clustering_method = "average",
clustering_distance_rows = "correlation",
clustering_distance_cols = "correlation",
cutree_cols = 3, cutree_rows = 2,
breaks = seq(-3, 3, 0.05),
color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
annotation_colors = color_list,
annotation_col = pData(target_Data)[, ann_names])
}

8 Pathway Analysis
Pathway analysis enables exploration of different aggregate gene sets
for our experimental questions. Each individual ROI/AOI segment is
scored for every pathway of interest, which we can then use to
investigate biological differences. We will perform analogous analyses
as those outlined in the Differential Expression and Spatial
Deconvolution sections of the report for gene set enrichment.
8.1 Scoring Gene Sets
Pathways and gene sets were defined from the Kegg Brite database. We
use an R software package called Gene Set Variation Analysis to score
each segment within our study. see https://cran.r-project.org/web/packages/msigdbr/vignettes/msigdbr-intro.html
for options on collections. We use the KEGG and REACTOME.
#gc()
h_gene_sets = rbind(msigdbr(species = "human", subcategory = "CP:KEGG"),
msigdbr(species = "human", subcategory = "CP:REACTOME"))
#msigdbr(species = "human", subcategory = "CP:BIOCARTA")
msigdbr_list = split(x = h_gene_sets$gene_symbol, f = h_gene_sets$gs_name)
# prepare df for accurate merging back genes later
pw_gene_df<-data.frame(Pathway = names(msigdbr_list))
pw_gene_df$genes<-msigdbr_list
ssgsea_results <- GSVA::gsva(expr = assayDataElement(target_Data,
elt = "log_q"),
gset.idx.list = msigdbr_list,
method = "ssgsea",
min.sz = 5,
max.sz = 500,
verbose = FALSE)
## [1] "Calculating ranks..."
## [1] "Calculating absolute values from ranks..."
## [1] "Normalizing..."
geneSetObj <-
NanoStringGeoMxSet(assayData = ssgsea_results,
phenoData = AnnotatedDataFrame(pData(target_Data)),
protocolData = protocolData(target_Data),
featureType = "GeneSet",
check = FALSE)
8.2 Differental analysis of pathways
# # convert test variables to factors
pData(target_Data)$testClass <- factor(pData(target_Data)$ANN1, unique(target_Data$ANN1))
pData(geneSetObj)[["slide"]]<-factor(pData(geneSetObj)[["slide_name"]])
#pData(target_Data)$testRegion<-factor(pData(target_Data)$ANN2, unique(count_mat$ANN3))
lmm_ssgsea_results <- c()
for (region in unique(pData(target_Data)$ANN3)) {
ind <- pData(target_Data)$ANN3 == region
mixedOutmc <-
mixedModelDE(geneSetObj[, ind],
elt = "exprs",
modelFormula = ~ testClass + (1 | slide),
groupVar = "testClass",
nCores = parallel::detectCores(),
multiCore = TRUE)
# format results as data.frame
r_ssgsea_test <- do.call(rbind, mixedOutmc["lsmeans", ])
ssgsea_tests <- rownames(r_ssgsea_test)
r_ssgsea_test <- as.data.frame(r_ssgsea_test)
r_ssgsea_test$Contrast <- ssgsea_tests
#r_ssgsea_test$Genes <- msigdbr_list seems unreliable as gsva omits pathways if genes are not in data..
# use lapply in case you have multiple levels of your test factor to
# correctly associate gene name with it's row in the results table
r_ssgsea_test$Pathway <-
unlist(lapply(colnames(mixedOutmc),
rep, nrow(mixedOutmc["lsmeans", ][[1]])))
#r_ssgsea_test$Subset <- status
r_ssgsea_test$FDR <- p.adjust(r_ssgsea_test$`Pr(>|t|)`, method = "fdr")
r_ssgsea_test$Subset <- region
r_ssgsea_test <- r_ssgsea_test[, c("Pathway", "Subset", "Contrast", "Estimate",
"Pr(>|t|)", "FDR")]
lmm_ssgsea_results <- rbind(lmm_ssgsea_results, r_ssgsea_test)
}
lmm_ssgsea_results <- merge(lmm_ssgsea_results, pw_gene_df)
8.2.1 Table of Differental analysis of pathways
datatable(subset(lmm_ssgsea_results), rownames = FALSE,
extensions = 'Buttons', options = list (
pageLength = 10,
dom = 'Bftrip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
),
caption = "DE results for Pathways",filter='top') %>% formatRound(columns=c("Estimate","Pr(>|t|)","FDR"), digits=5)
8.3 Vulcanoplot of LMM_Pathways
# Categorize Results based on P-value & FDR for plotting
fc_threshold = 0.3
lmm_ssgsea_results$Color <- "NS or FC < 0.3"
lmm_ssgsea_results$Color[lmm_ssgsea_results$`Pr(>|t|)` < 0.05] <- "P < 0.05"
lmm_ssgsea_results$Color[lmm_ssgsea_results$FDR < 0.05] <- "FDR < 0.05"
lmm_ssgsea_results$Color[lmm_ssgsea_results$FDR < 0.001] <- "FDR < 0.001"
lmm_ssgsea_results$Color[abs(lmm_ssgsea_results$Estimate) < fc_threshold] <- "NS or FC < 0.3"
lmm_ssgsea_results$Color <- factor(lmm_ssgsea_results$Color,
levels = c("NS or FC < 0.3", "P < 0.05",
"FDR < 0.05", "FDR < 0.001"))
# pick top pw for either side of volcano to label
# order pw for convenience:
lmm_ssgsea_results$invert_P <- (-log10(lmm_ssgsea_results$`Pr(>|t|)`)) * sign(lmm_ssgsea_results$Estimate)
top_ssgsea_g <- c()
#loop here over tested conditions if applicable
for(c in unique(lmm_ssgsea_results$Subset) ) {
lmm_results_slice = lmm_ssgsea_results[lmm_ssgsea_results$Subset == c,]
top_ssgsea_g <- c(top_ssgsea_g,
lmm_ssgsea_results[ind, 'Pathway'][
order(lmm_ssgsea_results[ind, 'invert_P'], decreasing = TRUE)[1:15]],
lmm_ssgsea_results[ind, 'Pathway'][
order(lmm_ssgsea_results[ind, 'invert_P'], decreasing = FALSE)[1:15]])
}
top_ssgsea_g <- unique(top_ssgsea_g)
lmm_ssgsea_results <- lmm_ssgsea_results[, -1*ncol(lmm_ssgsea_results)] # remove invert_P from matrix
#lmm_ssgsea_results_slice <- lmm_ssgsea_results_slice[lmm_ssgsea_results_slice$FDR < 1,]
# Graph results
ggplot(lmm_ssgsea_results,
aes(x = Estimate, y = -log10(`Pr(>|t|)`),
color = Color, label = Pathway)) +
geom_vline(xintercept = c(0.2, -0.2), lty = "dashed") +
geom_hline(yintercept = -log10(0.05), lty = "dashed") +
geom_point() +
labs(x = paste(lmm_results_slice$Contrast, "log2(FC)"),
y = "Significance, -log10(P)",
color = "Significance") +
scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
`FDR < 0.05` = "lightblue",
`P < 0.05` = "orange2",
`NS or FC < 0.5` = "gray"),
guide = guide_legend(override.aes = list(size = 4))) +
scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
geom_text_repel(data = subset(lmm_ssgsea_results, Color == "FDR < 0.05" | Color == "FDR < 0.001"),
point.padding = 0.15, color = "black",size=3,
min.segment.length = .1, box.padding = .2, lwd = 2,
max.overlaps = 50) +
theme_bw(base_size = 16) +
theme(legend.position = "bottom") +
facet_wrap(~Subset, scales = "free_y")

# +facet_wrap(~Subset, scales = "free_y"))
8.4 heatmap of pathways
active_pw<-filter(lmm_ssgsea_results, `Pr(>|t|)` < 0.001)$Pathway
active_pw<-filter(lmm_ssgsea_results, FDR < 0.001 )$Pathway
#active_pw<-filter(lmm_ssgsea_results, Color == "FDR < 0.001")$Pathway
active_pw<-top_ssgsea_g
if (length(active_pw)>1) {
print("go")
pw_matrix<-assayDataElement(geneSetObj, elt = "exprs")
pheatmap(pw_matrix[active_pw,],
scale = "row",
show_rownames = TRUE, show_colnames = TRUE,
fontsize_row = 10,
border_color = NA,
clustering_method = "average",
#clustering_distance_rows = "correlation",
clustering_distance_cols = "euclidean",
cutree_cols = 2, cutree_rows = 2,
breaks = seq(-3, 3, 0.05),
#color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
main = "Heatmap of selected Pathways",
annotation_colors = color_list,
annotation_col = pData(target_Data)[, ann_names])
}else{
print("No significant results to display")
}
## [1] "go"

8.5 fgsea pathway analysis
Another option for pathway analysis is the R software package called
Fast Gene Set Enrichment Analysis. It is a pre-ranked method that uses
the LMM results combined with the same pathway list from msigdbr as
GSVA.
fgsea_results_all <- c()
for (contrast in unique(lmm_results$Contrast)) {
ranks <- lmm_results$Estimate[lmm_results$Contrast == contrast]
names(ranks) <- lmm_results$Gene[lmm_results$Contrast == contrast]
fgsea_results <- fgsea(msigdbr_list,
ranks,
eps = 0.0,
minSize=15,
maxSize = 500)
fgsea_results$Contrast <- contrast
fgsea_results_all <- rbind(fgsea_results_all, fgsea_results)
}
fgsea_reactome <- fgsea_results_all[grep("REACTOME", pathway),]
fgsea_reactome$pathway <- gsub("REACTOME_", "", fgsea_reactome$pathway)
fgsea_reactome$pathway <- gsub("_", " ", fgsea_reactome$pathway)
# Categorize Results based on P-value & FDR for plotting
fc_threshold = 0.3
fgsea_results_all$Color <- "NS or FC < 0.3"
fgsea_results_all$Color[fgsea_results_all$pval < 0.05] <- "P < 0.05"
fgsea_results_all$Color[fgsea_results_all$padj < 0.05] <- "FDR < 0.05"
fgsea_results_all$Color[fgsea_results_all$padj < 0.001] <- "FDR < 0.001"
fgsea_results_all$Color[abs(fgsea_results_all$ES) < fc_threshold] <- "NS or FC < 0.3"
fgsea_results_all$Color <- factor(fgsea_results_all$Color,
levels = c("NS or FC < 0.3", "P < 0.05",
"FDR < 0.05", "FDR < 0.001"))
plt <- htmltools::tagList()
counter = 1
for(c in unique(fgsea_results_all$Contrast) ) {
fgsea_results_slice = fgsea_results_all[fgsea_results_all$Contrast == c,]
# # pick top genes for either side of volcano to label
# # order genes for convenience:
fgsea_results_slice$invert_P <- (-log10(fgsea_results_slice$pval)) * sign(fgsea_results_slice$ES)
# #loop here over tested conditions if applicable
#top_fgsea_g <- c()
top_fgsea_g <- c(fgsea_results_slice[, 'pathway'][
order(fgsea_results_slice[, 'invert_P'], decreasing = TRUE)[1:15]],
fgsea_results_slice[, 'pathway'][
order(fgsea_results_slice[, 'invert_P'], decreasing = FALSE)[1:15]])
# for (celltype in unique(lmm_results$Subset)) {
# top_fgsea_g <- c(top_fgsea_g,
# fgsea_results_slice[fgsea_results_slice$Subset==celltype, 'pathway'][
# order(fgsea_results_slice[, 'invert_P'], decreasing = TRUE)[1:20]],
# fgsea_results_slice[fgsea_results_slice$Subset==celltype, 'pathway'][
# order(fgsea_results_slice[, 'invert_P'], decreasing = FALSE)[1:20]])
# }
top_fgsea_g <- unique(top_fgsea_g)
#fgsea_results_slice <- fgsea_results_slice[, -1*ncol(fgsea_results_slice)] # remove invert_P from matrix
# Graph results
dynplot <- ggplot(fgsea_results_slice,
aes(x = NES, y = -log10(pval),
color = Color, label = pathway)) +
geom_vline(xintercept = c(fc_threshold,-fc_threshold), lty = "dashed") +
geom_hline(yintercept = -log10(0.05), lty = "dashed") +
geom_point() +
labs(x = paste(c, " FC"),
y = "Significance, -log10(P)",
color = "Significance") +
scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
`FDR < 0.05` = "lightblue",
`P < 0.05` = "orange2",
`NS or FC < 0.3` = "gray"),
guide = guide_legend(override.aes = list(size = 4))) +
scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
geom_text_repel(data = subset(fgsea_results_slice, Color == "P < 0.05" | Color == "FDR < 0.05" | Color == "FDR < 0.001"),
point.padding = 0.15, color = "black",size=5,
min.segment.length = .1, box.padding = .2, lwd = 2,
max.overlaps = 50) +
theme_bw(base_size = 16) +
theme(legend.position = "bottom") #+
#facet_wrap(~Subset, scales = "free_y")
plt[[counter]] <- as_widget(ggplotly(dynplot))
counter <- counter + 1
#datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
}
plt
8.6 Visualize KEGG pathway
Get pathway visualization from KEGG. clusterProfiler grabs the KEGG
pathway IDs and combined with pathview it can download a pathway image
from the KEGG database. The LMM results are given with the pathway id to
visualize up and down regulated genes.
get_wp_gmtfile <- function() {
wpurl <- 'https://wikipathways-data.wmcloud.org/current/gmt/'
x <- readLines(wpurl)
y <- x[grep('\\.gmt',x)]
sub(".*(wikipathways-.*\\.gmt).*", "\\1", y[grep('File', y)])
}
get_wp_data <- function(organism) {
organism <- sub(" ", "_", organism)
gmtfile <- get_wp_gmtfile()
wpurl <- 'https://wikipathways-data.wmcloud.org/current/gmt/'
url <- paste0(wpurl,
gmtfile[grep(organism, gmtfile)])
f <- tempfile(fileext = ".gmt")
dl <- mydownload(url, destfile = f)
if (is.null(f)) {
message("fail to download wikiPathways data...")
return(NULL)
}
read.gmt.wp(f)
}
# enrichKEGG grab online pathways
gene <- target_Data@featureData@data[["GeneID"]]
kk <- enrichKEGG(gene = gene,
organism = 'hsa',
pvalueCutoff = 0.1)
## Reading KEGG annotation online: "https://rest.kegg.jp/link/hsa/pathway"...
## Reading KEGG annotation online: "https://rest.kegg.jp/list/pathway/hsa"...
enrichKEGG_results <- kk@result
# Match column values for link
# fsgea run
fgsea_results$Description <- gsub("KEGG_", "", fgsea_results$pathway)
fgsea_results$Description <- gsub("_", " ", fgsea_results$Description)
# ssgea run
# lmm_ssgsea_results_d$Description <- gsub("KEGG_", "", lmm_ssgsea_results_d$Pathway)
# lmm_ssgsea_results_d$Description <- gsub("_", " ", lmm_ssgsea_results_d$Description)
enrichKEGG_results$Description <- toupper(enrichKEGG_results$Description)
# Create df for matching KEGG results
KEGGIDs <- fgsea_results#[fgsea_results$Contrast == "Pro_Tub - Dis_Tub"]
KEGGIDs <- KEGGIDs %>% inner_join(enrichKEGG_results, by = 'Description') %>% select(Description, ID, geneID, leadingEdge, padj)
# Create geneList with DE results for visualization pathway genes
genelist <- lmm_results$Estimate
names(genelist) <- target_Data@featureData@data[["GeneID"]]
# Choose manual or highest significant pathway
pathway_name <- "INSULIN SIGNALING PATHWAY" # manual pathway query
pathway_name <- toupper(pathway_name)
if (pathway_name == "") {
pathway_name <- KEGGIDs[order(KEGGIDs$padj, decreasing = FALSE), ][1]$Description
}
pathwayid <- KEGGIDs$ID[KEGGIDs$Description == pathway_name]
# Grab pathway image with gene info, save and plot
viewPath <- pathview(gene.data = genelist,
pathway.id = pathwayid,
species = "hsa",
out.suffix = "genesinfo", kegg.native = T, same.layer = F)
## Info: Downloading xml files for hsa04910, 1/1 pathways..
## Info: Downloading png files for hsa04910, 1/1 pathways..
## 'select()' returned 1:1 mapping between keys and columns
## Info: Working in directory L:/pkloosterman/Github/DKD_Kidney
## Info: Writing image file hsa04910.genesinfo.png
img <- readPNG(paste(pathwayid, ".genesinfo.png", sep = ""))
grid::grid.raster(img)

Barplot pathways
Clear vizualization of the top 15 positive and negative expressed
Reactome pathways. Pathways with a adjusted p-value of 0.05 or higher
will be presented as red and below 0.05 as green.
top_all <- c()
for (contrast in unique(fgsea_reactome$Contrast)) {
# active_group1 <- contrast_list[contrast][[1]][[1]]
# active_group2 <- contrast_list[contrast][[1]][[2]]
# ind <- pData(target_Data)[pData(target_Data)$ANN2 == active_group1 | pData(target_Data)$ANN2 == active_group2]
top <- fgsea_reactome[fgsea_reactome$Contrast == contrast]
toppos <- top[order(top$NES, decreasing = TRUE)][0:15]
topneg <- top[order(top$NES, decreasing = FALSE)][0:15]
top <- rbind(toppos, topneg)
top$Color[top$padj < 0.05] <- "padj < 0.05"
top$Color[top$padj == 0.05 | top$padj > 0.05] <- "padj > 0.05"
top_all <- rbind(top_all, top)
barplot <- ggplot(top,aes(x=reorder(pathway, NES),y=NES,fill=Color)) +
scale_x_discrete(labels = function(x) str_wrap(x, width = 100)) +
geom_col(position="dodge") + scale_fill_manual(values = c("#2ECC71", "#E74C3C")) +
ggtitle(paste("Top 15 + and - NES reactome pathways from", contrast)) +
xlab("pathway") + ylab("Normalized Enrichment Score") +
coord_flip() +
theme(text = element_text(size = 20)) +
theme(panel.background = element_blank())
#facet_wrap(~Contrast, scales = "free_y")
print(barplot)
}

9 Spatial Deconvolution
9.1 Calculate backgrounds
#gc()
bg = derive_GeoMx_background(
norm = assayDataElement(target_Data , elt = "q_norm"),
probepool = fData(target_Data)$Module,
negnames = c("NegProbe-CTP01","NegProbe-Kilo","Negative Probe", "NegProbe-WTX" ))
#negnames = "NegProbe-WTX")
9.2 Load cell profile
A “cell profile matrix” is a pre-defined matrix that specifies the
expected expression profiles of each cell type in the experiment. The
SpatialDecon library comes with one such matrix pre-loaded, the
“SafeTME” matrix, designed for estimation of immune and stroma cells in
the tumor microenvironment. (This matrix was designed to avoid genes
commonly expressed by cancer cells; see the SpatialDecon manuscript for
details.). Otherwise, load specific profiles from https://github.com/Nanostring-Biostats/CellProfileLibrary/tree/NewProfileMatrices
#safeTME
data("safeTME")
data("safeTME.matches")
current_cell_profile<-safeTME
#see: https://github.com/Nanostring-Biostats/CellProfileLibrary/tree/NewProfileMatrices
current_cell_profile <- download_profile_matrix(species = "Human",
age_group = "Adult",
matrixname = "Kidney_HCA")
heatmap(sweep(current_cell_profile, 1, apply(current_cell_profile, 1, max), "/"),
labRow = NA, margins = c(10, 5), cexCol = 0.7)

9.3 Run spatial deconvolution
# vector identifying pure tumor segments:
#target_Data$istumor = target_Data$ANN3 == "CORE" & target_Data$ANN1 == "PanCK+"
res = runspatialdecon(object = target_Data,
norm_elt = "q_norm",
raw_elt = "exprs",
#is_pure_tumor = target_Data$istumor,
cell_counts = target_Data$nuclei,
X = current_cell_profile,
#cellmerges = safeTME.matches, # safeTME.matches object, used by default
#n_tumor_clusters = 5, # how many distinct tumor profiles to append to safeTME
align_genes = TRUE)
9.3.1 Spatial deconvolution
heatmaps
Abundance
# NOTE: check clustering.. why different?
#set display thresholds
thresh <- signif(quantile(res$beta, 0.97), 2)
# plot stored to keep clustering for later
p1<-pheatmap(pmin(t(res$beta),thresh),
#scale = "row",
cutree_cols = 3,
cutree_rows = 2,
fontsize_row = 12,
show_rownames = TRUE, show_colnames = TRUE,
angle_col = "90",
border_color = NA,
#clustering_method = "average",
#clustering_distance_rows = "correlation",
#clustering_distance_cols = "correlation",
legend_breaks = c(round(seq(0, thresh, length.out = 5))[-5], thresh),
legend_labels = c(round(seq(0, thresh, length.out = 5))[-5], paste0("Abundance scores,\ntruncated above at ", thresh)),
#breaks = seq(0, 5, 1),
color = colorRampPalette(c("white","darkblue"))(100),
annotation_colors = color_list,
annotation_col = pData(target_Data)[, ann_names]
)

#p1
Proportional
# proportions:
props <- replace(res$prop_of_nontumor, is.na(res$prop_of_nontumor), 0)
p2<-pheatmap(t(props),
#scale = "row",
cutree_cols = 3,
cutree_rows = 2,
fontsize_row = 12,
show_rownames = TRUE, show_colnames = TRUE,
angle_col = "90",
border_color = NA,
#clustering_method = "average",
#clustering_distance_rows = "correlation",
#clustering_distance_cols = "correlation",
legend_breaks = round(seq(0, max(props) * 0.99, length.out = 5), 2),
legend_labels = c(round(seq(0, max(props), length.out = 5), 2)[-5], "Proportion of all\nfitted populations"),
color = colorRampPalette(c("white","darkblue"))(100),
annotation_colors = color_list,
annotation_col = pData(target_Data)[, ann_names])

#p2
Scaled
# scaled abundances:
epsilon <- min(res$beta[res$beta > 0])
mat <- sweep(res$beta, 1, pmax(apply(res$beta, 1, max), epsilon), "/")
pheatmap(t(mat),
#scale = "row",
cutree_cols = 3,
cutree_rows = 3,
fontsize_row = 12,
show_rownames = TRUE, show_colnames = TRUE,
angle_col = "90",
border_color = NA,
#clustering_method = "average",
#clustering_distance_rows = "correlation",
#clustering_distance_cols = "correlation",
legend_breaks = c(round(seq(0, 1, length.out = 5), 2)[-5], 1),
legend_labels = c(round(seq(0, 1, length.out = 5), 2)[-5], "Scaled abundance\n(ratio to max)"),
color = colorRampPalette(c("white","darkblue"))(100),
annotation_colors = color_list,
annotation_col = pData(target_Data)[, ann_names])

9.4 Barplots
abundance
# define variables to show in heatmaps:
pData(target_Data)$region <-
factor(pData(target_Data)$ANN3, unique(target_Data$ANN3)) # Must be manual?
pData(target_Data)$class <-
factor(pData(target_Data)$ANN1, unique(target_Data$ANN1)) # Must be manual?
variables_to_plot <- c("slide_name", "ANN1", "ANN3") # Must be manual?
#define colors
# only for safeTME colors
#col <- cellcols
#custom celmatrix
#get large number of colors
qual_col_pals = brewer.pal.info[brewer.pal.info$category == 'qual',]
col_vector = unique(unlist(mapply(brewer.pal, qual_col_pals$maxcolors, rownames(qual_col_pals))))
celltypes<-sample(col_vector,length(colnames(res$beta)))
names(celltypes)<-colnames(res$beta)
col<-celltypes
#tempfix for abbreviated and now mismatching annotations
#can just use ann dataframe?
#tmpann<-cbind(ANN1,ANN2,SN)
tmpann <- pData(target_Data)[ann_names]
layout(matrix(c(1, 2, 3, 3), nrow = 2),
widths = c(10, 3, 10, 3),
heights = c(1, 8, 10),
)
par(mar = c(0, 8.2, 0, 0.2))
plot(p1$tree_col, labels = F, main = "", ylab = "", yaxt = "n")
par(mar = c(15, 8, 0, 0))
# data to plot:
mat <- t(res$beta)[, p1$tree_col$order]
# infer scale of negative y-axis for annotation colorbars
ymin <- -max(colSums(mat)) * 0.15
if (!is.finite(ymin)) {
ymin <- 0
}
# draw barplot:
bp <- barplot(mat,
cex.lab = 1.5,
col = col, border = NA,
cex.names = 1.1,
las = 2, main = "", ylab = "Abundance scores",
ylim = c(ymin, max(colSums(mat)))
)
# add color bars for annotations
for (name in rev(variables_to_plot)) {
yrange <- seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 1)[match(name, variables_to_plot) + c(0, 1)]
xwidth <- (bp[2] - bp[1]) / 2
for (i in 1:ncol(mat)) {
rect(bp[i] - xwidth, yrange[2], bp[i] + xwidth, yrange[1],
# border = NA, col = ann_colors[[name]][segmentAnnotations[match(colnames(mat)[i], segmentAnnotations$segmentID), name]]
#border = NA, col = ann_colors[[name]][ann[p1$tree_col$order[i], name]]
border = NA, col = color_list[[name]][tmpann[colnames(mat)[i], name]]
)
}
}
axis(2,
at = seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 2)[-c(1, length(variables_to_plot) + 2)],
las = 2, labels = variables_to_plot, lty = 0, cex.axis = 1.2
)
#draw a legend:
par(mar = c(0.1, 0.1, 0.1, 0.1))
frame()
legendcols <- legendnames <- c()
#for (name in rev(names(ann_colors))) {
for (name in c("slide_name", "ANN1", "ANN3")) {
legendcols <- c(legendcols, NA, color_list[[name]], NA)
legendnames <- c(legendnames, name, names(color_list[[name]]), NA)
}
legend("center",
pch = 15,
cex = 1.5,
col = c(legendcols, rep(NA, 1), rev(col)),
legend = c(legendnames, "Cell type", rev(names(col))),
)

proportional
# define variables to show in heatmaps:
variables_to_plot <- c("slide_name", "ANN1", "ANN3")
layout(matrix(c(1, 2, 3, 3), nrow = 2),
widths = c(10, 3, 10, 3),
heights = c(1, 8, 10),
)
par(mar = c(0, 8.2, 0, 0.2))
plot(p2$tree_col, labels = F, main = "", ylab = "", yaxt = "n")
par(mar = c(15, 8, 0, 0))
# data to plot:
mat <- t(res$prop_of_nontumor)[, p2$tree_col$order]
mat <- replace(mat, is.na(mat), 0)
# infer scale of negative y-axis for annotation colorbars
ymin <- -0.15
# draw barplot:
bp <- barplot(mat,
cex.lab = 1.5,
col = col, border = NA,
cex.names = 1.1,
las = 2, main = "", ylab = "Proportion of fitted cells",
ylim = c(ymin, max(colSums(mat)))
)
# add color bars for annotations
for (name in rev(variables_to_plot)) {
yrange <- seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 1)[match(name, variables_to_plot) + c(0, 1)]
xwidth <- (bp[2] - bp[1]) / 2
for (i in 1:ncol(mat)) {
rect(bp[i] - xwidth, yrange[2], bp[i] + xwidth, yrange[1],
# border = NA, col = ann_colors[[name]][segmentAnnotations[match(colnames(mat)[i], segmentAnnotations$segmentID), name]]
#border = NA, col = ann_colors[[name]][ann[p2$tree_col$order[i], name]]
border = NA, col = color_list[[name]][tmpann[colnames(mat)[i], name]]
)
}
}
axis(2,
at = seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 2)[-c(1, length(variables_to_plot) + 2)],
las = 2, labels = variables_to_plot, lty = 0, cex.axis = 1.2
)
#draw a legend:
par(mar = c(0.1, 0.1, 0.1, 0.1))
frame()
legendcols <- legendnames <- c()
#for (name in rev(names(ann_colors))) {
for (name in c("slide_name", "ANN1", "ANN3")) {
legendcols <- c(legendcols, NA, color_list[[name]], NA)
legendnames <- c(legendnames, name, names(color_list[[name]]), NA)
}
legend("center",
pch = 15,
cex = 1.4,
col = c(legendcols, rep(NA, 1), rev(col)),
legend = c(legendnames, "Cell type", rev(names(col))),
)

11 CNV
Copy Number Variation analysis with the R software package inferCNV
(https://github.com/broadinstitute/inferCNV/wiki).
InferCNV is used to explore tumor single cell RNA-Seq data to
identify evidence for somatic large-scale chromosomal copy number
alterations, such as gains or deletions of entire chromosomes or large
segments of chromosomes. This is done by exploring expression intensity
of genes across positions of tumor genome in comparison to a set of
reference ‘normal’ cells. A heatmap is generated illustrating the
relative expression intensities across each chromosome, and it often
becomes readily apparent as to which regions of the tumor genome are
over-abundant or less-abundant as compared to that of normal cells.
Per patient a cnv analysis with a provided reference group. The gene
order is made from the projects .pkc file.
Select the annotation where the CNV analysis should look at
specifically as group and specify a subgroup as group_filter if the
group of interest is a subgroup inside the annotation. For example if
the CNV analysis needs to be only on tumor regions the group would be
ANNX(ANN column with the info about the tumor regions) and group_filter
PanCK+. The annotation that references the patients as patients. If
there are no patients leave the parameter empty as ““. The annotation
that should be included in the results including a reference set as
cnv_target. This will create patient specific files with all the
information inferCNV needs.
Select the reference set in reference. This can be more than one.
Every patient without its own reference will not be analysed.
#########PARAMETERS########
reference <- c("normal")
###########################
failed <- c()
cnv_list <- list()
for (patient in patient_list) {
# Name output folder
out_dir <- paste0(patient, "_CNV")
if (str_detect(paste(readLines(paste0(patient, ".Annotations.tsv")), collapse = ''), reference) == FALSE) {
failed <- c(failed, patient)
next
}
# Create the infercnv object
infercnv_obj = CreateInfercnvObject(raw_counts_matrix= paste0(patient, ".Counts.tsv"),
annotations_file= paste0(patient, ".Annotations.tsv"),
delim="\t",
gene_order_file= "gene_order_Hs_WTA_v1_pkc.txt",
#gene_order_file= "gencode_v19_gene_pos.txt",
ref_group_names= reference, # input the normal/reference group names
chr_exclude = c("chrM"))#c("chrM")) # Default excludes chrX, chrY and chrM. By only picking chrM you include the X and Y chromosomes.
# perform infercnv operations to reveal cnv signal. For all options: https://rdrr.io/github/broadinstitute/infercnv/man/run.html
#cnv_list[[patient]]
infercnv_obj <- infercnv::run(infercnv_obj,
cutoff=0.1, # use 1 for smart-seq, 0.1 for 10x-genomics
out_dir= out_dir, # dir is auto-created for storing outputs
cluster_by_groups=FALSE, # cluster
denoise=TRUE,
HMM=FALSE,
tumor_subcluster_partition_method = c("qnorm"),
analysis_mode = "subclusters",
no_plot=TRUE
#,debug=TRUE
)
plot_cnv(infercnv_obj,
out_dir = paste0(patient, "_CNV"),
title = "inferCNV",
obs_title = "Observations (Cells)",
ref_title = "References (Cells)",
cluster_by_groups = FALSE,
cluster_references = FALSE,
plot_chr_scale = FALSE,
#chr_lengths = NULL,
k_obs_groups = 1,
contig_cex = 1.5,
#x.center = mean(infercnv_obj@expr.data),
x.range = "auto",
#hclust_method = "ward.D",
output_filename = "infercnv",
output_format = "png",
png_res = 300
)
}
for (patient in patient_list) {
print(paste(group_filter, patient))
if (patient %in% failed) {
print(" ^ Patient contained no reference group")
next
}
img <- readPNG(paste(paste0(patient, "_CNV"), "/infercnv.png", sep = ""))
grid::grid.newpage()
grid::grid.raster(img)
}
## [1] " dummy"

11.1 Dendrogram
Closer look at the dendrogram from the CNV analysis per patient where
the nodes as written numbers. Use the numbers to select subgroups for
further analysis in ‘t-test on two subgroups’.
#out_dir <- "T1_NANO_012_CNV"
trees <- list()
for (patient in patient_list) {
if (patient %in% failed) {
#print("Patient contained no reference group")
next
}
tree <- read.tree(paste(patient,"_CNV/infercnv.observations_dendrogram.txt", sep = ""))
obv <- read.csv(paste(patient,"_CNV/infercnv.observation_groupings.txt", sep = ""), sep="")
trees[[patient]] <- ggtree(
tree, ladderize=F) +
geom_treescale() +
geom_tiplab(color=obv$Annotation.Color, hjust=-.2) +
coord_cartesian(clip = 'off') +
theme_tree2(plot.margin=margin(6, 200, 6, 6)) +
geom_text2(aes(label=node), hjust=-.3, size = 3) +
ggplot2::labs(title = patient)
}
grid.arrange(grobs=trees,ncol=3)

t-test chr with groups
Compare the contrast within a chromosome. Select a chromosome,
contrast, and patient of interest to run a t-test on.
#########PARAMETERS########
chr <- "chrX" # Select chromosome of interest
contrast <- c("normal", "DKD") # Select contrast
patient <- "dummy"
###########################
positions <- as.data.frame(positions)
colnames(positions) <- c("gene", "chr", "begin", "end")
select_genes <- positions$gene[positions$chr == chr] # Grab chr specific genes
annotation <- as.data.frame(read.delim(paste0(patient, ".Annotations.tsv"), header=FALSE))
colnames(annotation) <- c("Sample_ID", "ANN")
select_samples <- annotation
filter_counts <- as.data.frame(target_Data@assayData[["log_q"]])
colnames(filter_counts) <- gsub('.dcc','', colnames(filter_counts))
filter_counts <- filter_counts[,select_samples$Sample_ID] # filter out samples that are not the interesting region
select_genes <- select_genes[select_genes %in% rownames(filter_counts)] # Only use genes that are actually in the data (pkc gene file has all of them)
filter_counts <- filter_counts[select_genes,] # filter out non chromosome specific genes
plots<-list()
tables<-list()
labels<-list()
test<-"ttest"
mtc<-"BH"
counter=1
log_q_filter <-as.data.frame(filter_counts)
comps_df<-data.frame(comp='',val='')
for (active_group1 in contrast) {
for (active_group2 in contrast) {
#supress reduncant compares
if(active_group1==active_group2) {next}
comp<-paste(sort(c(active_group1,active_group2)),collapse = "_")
#print(comp)
if(comp %in% comps_df$comp) {next}
temp_df<-data.frame(comp=comp ,val=1)
comps_df<-rbind(comps_df,temp_df)
labels[[counter]]<-paste(active_group1," vs ", active_group2)
group1<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group1]]
group2<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group2]]
#run t_tests
results<-as.data.frame ( apply(log_q_filter, 1, function(x) t.test(x[colnames(group1)],x[colnames(group2)])$p.value) )
colnames(results)<-"raw_p_value"
#multiple_testing_correction
adj_p_value<- p.adjust(results$raw_p_value,method=mtc)
results<-cbind(results,adj_p_value)
#calc_fdr
FDR<- p.adjust(results$raw_p_value,method="fdr")
results<-cbind(results,FDR)
#fold_changes
#as base data is already log transformed, means need to be subtracted to get FC in log space
fchanges<-as.data.frame( apply(log_q_filter, 1, function(x) (mean(x[colnames(group1)]) - mean(x[colnames(group2)]) ) ) )
colnames(fchanges)<-"FC"
results<-cbind(results,fchanges)
#add genenames
results$Gene<-rownames(results)
#set categories based on P-value & FDR for plotting
results$Color <- "NS or FC < 0.5"
results$Color[results$adj_p_value < 0.05] <- "P < 0.05"
results$Color[results$FDR < 0.05] <- "FDR < 0.05"
results$Color[results$FDR < 0.001] <- "FDR < 0.001"
results$Color[abs(results$FC) < 0.5] <- "NS or FC < 0.5"
results$Color <- factor(results$Color,
levels = c("NS or FC < 0.5", "P < 0.05", "FDR < 0.05", "FDR < 0.001"))
#vulcanoplot
# pick top genes for either side of volcano to label
# order genes for convenience:
results$invert_P <- (-log10(results$adj_p_value)) * sign(results$FC)
top_g <- c()
top_g <- c(top_g,
results[ind, 'Gene'][
order(results[ind, 'invert_P'], decreasing = TRUE)[1:15]],
results[ind, 'Gene'][order(results[ind, 'invert_P'], decreasing = FALSE)[1:15]])
top_g<- unique(top_g)
results <- results[, -1*ncol(results)] # remove invert_P from matrix
# Graph results
plots[[counter]]<- ggplot(results,
aes(x = FC, y = -log10(adj_p_value),
color = Color, label = Gene)) +
geom_vline(xintercept = c(0.5, -0.5), lty = "dashed") +
geom_hline(yintercept = -log10(0.05), lty = "dashed") +
geom_point() +
labs(x = paste("Enriched genes in", chr, "-", active_group2," <- log2(FC) -> Enriched in", active_group1),
y = "Significance, -log10(P)",
color = "Significance") +
scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
`FDR < 0.05` = "lightblue",
`P < 0.05` = "orange2",
`NS or FC < 0.5` = "gray"),
guide = guide_legend(override.aes = list(size = 4))) +
scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
geom_text_repel(data = subset(results, FDR<0.05 & (-0.5>FC| FC>0.5)),
point.padding = 0.15, color = "black", size=3.5,
min.segment.length = .1, box.padding = .2, lwd = 2,
max.overlaps = 50) +
theme_bw(base_size = 20) +
theme(legend.position = "bottom") +
#ggtitle(paste(slide,": ", test, mtc,"multitest corr"))
ggtitle(paste(patient, ": ", test, mtc,"multitest corr"))
#store tables for display later
tables[[counter]]<-results
counter = counter+1
#datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
}
}
grid.arrange(grobs=plots,ncol=2)

t-test on two subgroups
Select nodes from the dendrogram as subgroups, in this case nodes 18
and 15. Decide on the involved chromosomes and add them into the
chr_list. Specify the patient in patient.
#########PARAMETERS########
chr_list <- c("chr22") # Choose needed chromosomes of the target area. Think of it as the x-axis
patient <- "dummy"
node_interest <- 233 # Choose subgroup 1
node_interest2 <- 216 # Choose subgroup 2
###########################
tree <- read.tree(paste(patient,"_CNV/infercnv.observations_dendrogram.txt", sep = ""))
tree_info <- tree %>% as.treedata %>% as_tibble # transform tree into accessible data
samples_interest <- offspring(tree_info, node_interest) # Get labels attached to the group
samples_interest <- as.character(na.omit(samples_interest$label)) # formatting
paste("Subgroup 1: ", samples_interest)
## [1] "Subgroup 1: DSP-1001250007864-D-H08"
## [2] "Subgroup 1: DSP-1001250007864-D-H10"
## [3] "Subgroup 1: DSP-1001250007864-D-H06"
## [4] "Subgroup 1: DSP-1001250007864-D-H02"
## [5] "Subgroup 1: DSP-1001250007864-D-H04"
## [6] "Subgroup 1: DSP-1001250007864-D-H11"
## [7] "Subgroup 1: DSP-1001250007864-D-H12"
## [8] "Subgroup 1: DSP-1001250007864-D-H05"
## [9] "Subgroup 1: DSP-1001250007864-D-H07"
## [10] "Subgroup 1: DSP-1001250007864-D-H09"
## [11] "Subgroup 1: DSP-1001250007864-D-H01"
## [12] "Subgroup 1: DSP-1001250007864-D-H03"
## [13] "Subgroup 1: DSP-1001250007851-H-A09"
## [14] "Subgroup 1: DSP-1001250007851-H-C04"
## [15] "Subgroup 1: DSP-1001250007851-H-D06"
## [16] "Subgroup 1: DSP-1001250007851-H-C07"
## [17] "Subgroup 1: DSP-1001250007851-H-D12"
## [18] "Subgroup 1: DSP-1001250007851-H-A12"
## [19] "Subgroup 1: DSP-1001250007851-H-A03"
## [20] "Subgroup 1: DSP-1001250007851-H-D09"
## [21] "Subgroup 1: DSP-1001250007851-H-A04"
## [22] "Subgroup 1: DSP-1001250007851-H-D10"
## [23] "Subgroup 1: DSP-1001250007851-H-A10"
## [24] "Subgroup 1: DSP-1001250007851-H-C01"
## [25] "Subgroup 1: DSP-1001250007851-H-C03"
## [26] "Subgroup 1: DSP-1001250007851-H-A05"
## [27] "Subgroup 1: DSP-1001250007851-H-C02"
## [28] "Subgroup 1: DSP-1001250007851-H-B12"
## [29] "Subgroup 1: DSP-1001250007851-H-B06"
## [30] "Subgroup 1: DSP-1001250007851-H-B11"
## [31] "Subgroup 1: DSP-1001250007851-H-B04"
## [32] "Subgroup 1: DSP-1001250007851-H-B05"
## [33] "Subgroup 1: DSP-1001250007851-H-D11"
## [34] "Subgroup 1: DSP-1001250007851-H-D05"
## [35] "Subgroup 1: DSP-1001250007851-H-B07"
## [36] "Subgroup 1: DSP-1001250007851-H-C08"
## [37] "Subgroup 1: DSP-1001250007851-H-D08"
## [38] "Subgroup 1: DSP-1001250007851-H-B10"
## [39] "Subgroup 1: DSP-1001250007851-H-A02"
## [40] "Subgroup 1: DSP-1001250007851-H-A08"
## [41] "Subgroup 1: DSP-1001250007851-H-A06"
## [42] "Subgroup 1: DSP-1001250007851-H-A11"
## [43] "Subgroup 1: DSP-1001250007851-H-D04"
## [44] "Subgroup 1: DSP-1001250007851-H-D07"
## [45] "Subgroup 1: DSP-1001250007851-H-B01"
## [46] "Subgroup 1: DSP-1001250007851-H-B08"
## [47] "Subgroup 1: DSP-1001250007851-H-A07"
## [48] "Subgroup 1: DSP-1001250007851-H-B02"
## [49] "Subgroup 1: DSP-1001250007851-H-B03"
## [50] "Subgroup 1: DSP-1001250007851-H-C06"
## [51] "Subgroup 1: DSP-1001250007851-H-D01"
## [52] "Subgroup 1: DSP-1001250007851-H-C12"
## [53] "Subgroup 1: DSP-1001250007851-H-C09"
## [54] "Subgroup 1: DSP-1001250007851-H-C05"
## [55] "Subgroup 1: DSP-1001250007851-H-C11"
## [56] "Subgroup 1: DSP-1001250007851-H-C10"
## [57] "Subgroup 1: DSP-1001250007851-H-B09"
## [58] "Subgroup 1: DSP-1001250007851-H-D03"
## [59] "Subgroup 1: DSP-1001250007851-H-D02"
## [60] "Subgroup 1: DSP-1001250007868-B-A02"
samples_interest2 <- offspring(tree_info, node_interest2)
samples_interest2 <- as.character(na.omit(samples_interest2$label))
paste("Subgroup 2: ", samples_interest2)
## [1] "Subgroup 2: DSP-1001250007851-H-F10"
## [2] "Subgroup 2: DSP-1001250007851-H-F04"
## [3] "Subgroup 2: DSP-1001250007851-H-F11"
## [4] "Subgroup 2: DSP-1001250007851-H-F01"
## [5] "Subgroup 2: DSP-1001250007851-H-F05"
## [6] "Subgroup 2: DSP-1001250007851-H-E08"
## [7] "Subgroup 2: DSP-1001250007851-H-F03"
## [8] "Subgroup 2: DSP-1001250007851-H-F02"
## [9] "Subgroup 2: DSP-1001250007851-H-F09"
## [10] "Subgroup 2: DSP-1001250007851-H-F06"
## [11] "Subgroup 2: DSP-1001250007851-H-F07"
## [12] "Subgroup 2: DSP-1001250007868-B-A05"
## [13] "Subgroup 2: DSP-1001250007868-B-A03"
## [14] "Subgroup 2: DSP-1001250007868-B-B01"
## [15] "Subgroup 2: DSP-1001250007851-H-F12"
## [16] "Subgroup 2: DSP-1001250007868-B-A04"
## [17] "Subgroup 2: DSP-1002510007866-C-G02"
## [18] "Subgroup 2: DSP-1002510007866-C-H12"
positions <- as.data.frame(positions)
colnames(positions) <- c("gene", "chr", "begin", "end") # Formatting
select_genes <- positions$gene[positions$chr %in% chr_list] # | positions$chr == chr2] # Grab chromosome specific genes
annotation <- as.data.frame(read.delim(paste0(patient, ".Annotations.tsv"), header=FALSE))
colnames(annotation) <- c("Sample_ID", "ANN")
select_samples <- annotation[annotation$Sample_ID %in% samples_interest | annotation$Sample_ID %in% samples_interest2,] # Grab subgroup specific Sample IDs
filter_counts <- target_Data@assayData[["log_q"]]
colnames(filter_counts) <- gsub('.dcc','', colnames(filter_counts))
filter_counts <- filter_counts[,select_samples$Sample_ID] # Filter Sample ID's
select_genes <- select_genes[select_genes %in% rownames(filter_counts)] # Only use genes that are actually in the data (pkc gene file has all of them)
filter_counts <- filter_counts[select_genes,] # Filter genes
plots<-list()
tables<-list()
labels<-list()
test<-"ttest"
mtc<-"BH"
counter=1
log_q_filter <-as.data.frame(filter_counts)
comps_df<-data.frame(comp='',val='')
active_group1 <- samples_interest #subgroup 1
active_group2 <- samples_interest2 #subgroup 2
# for (active_group1 in c("sub1")) {
# for (active_group2 in c("sub2")) {
#supress reduncant compares
#if(active_group1==active_group2) {next}
#comp<-paste(sort(c(active_group1,active_group2)),collapse = "_")
#print(comp)
#if(comp %in% comps_df$comp) {next}
temp_df<-data.frame(comp=comp ,val=1)
comps_df<-rbind(comps_df,temp_df)
# labels[[counter]]<-paste(active_group1," vs ", active_group2)
# group1<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group1]]
# group2<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group2]]
labels[[counter]]<-paste(active_group1," vs ", active_group2)
group1<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$Sample_ID %in% active_group1]]
group2<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$Sample_ID %in% active_group2]]
#run t_tests
results<-as.data.frame ( apply(log_q_filter, 1, function(x) t.test(x[colnames(group1)],x[colnames(group2)])$p.value) )
colnames(results)<-"raw_p_value"
#multiple_testing_correction
adj_p_value<- p.adjust(results$raw_p_value,method=mtc)
results<-cbind(results,adj_p_value)
#calc_fdr
FDR<- p.adjust(results$raw_p_value,method="fdr")
results<-cbind(results,FDR)
#fold_changes
#as base data is already log transformed, means need to be subtracted to get FC in log space
fchanges<-as.data.frame( apply(log_q_filter, 1, function(x) (mean(x[colnames(group1)]) - mean(x[colnames(group2)]) ) ) )
colnames(fchanges)<-"FC"
results<-cbind(results,fchanges)
#add genenames
results$Gene<-rownames(results)
#set categories based on P-value & FDR for plotting
results$Color <- "NS or FC < 0.5"
results$Color[results$adj_p_value < 0.05] <- "P < 0.05"
results$Color[results$FDR < 0.05] <- "FDR < 0.05"
results$Color[results$FDR < 0.001] <- "FDR < 0.001"
results$Color[abs(results$FC) < 0.5] <- "NS or FC < 0.5"
results$Color <- factor(results$Color,
levels = c("NS or FC < 0.5", "P < 0.05", "FDR < 0.05", "FDR < 0.001"))
#vulcanoplot
# pick top genes for either side of volcano to label
# order genes for convenience:
results$invert_P <- (-log10(results$adj_p_value)) * sign(results$FC)
top_g <- c()
top_g <- c(top_g,
results[ind, 'Gene'][
order(results[ind, 'invert_P'], decreasing = TRUE)[1:15]],
results[ind, 'Gene'][order(results[ind, 'invert_P'], decreasing = FALSE)[1:15]])
top_g<- unique(top_g)
results <- results[, -1*ncol(results)] # remove invert_P from matrix
# Graph results
#plots[[counter]]<- ggplot(results,
p <- ggplot(results,
aes(x = FC, y = -log10(adj_p_value),
color = Color, label = Gene)) +
geom_vline(xintercept = c(0.5, -0.5), lty = "dashed") +
geom_hline(yintercept = -log10(0.05), lty = "dashed") +
geom_point() +
labs(x = paste("Enriched genes from", chr_list, "in", "subgroup 2"," <- log2(FC) -> Enriched in", "subgroup 1"),
y = "Significance, -log10(P)",
color = "Significance") +
scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
`FDR < 0.05` = "lightblue",
`P < 0.05` = "orange2",
`NS or FC < 0.5` = "gray"),
guide = guide_legend(override.aes = list(size = 4))) +
scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
geom_text_repel(data = subset(results, FDR<0.05 & (-0.5>FC| FC>0.5)),
point.padding = 0.15, color = "black", size=3.5,
min.segment.length = .1, box.padding = .2, lwd = 2,
max.overlaps = 50) +
theme_bw(base_size = 20) +
theme(legend.position = "bottom") +
#ggtitle(paste(slide,": ", test, mtc,"multitest corr"))
ggtitle(paste(patient, ": ", test, mtc,"multitest corr"))
#store tables for display later
tables[[counter]]<-results
counter = counter+1
#datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
# }
# }
#ggplotly(p)
p

#grid.arrange(grobs=plots,ncol=2)
LS0tDQp0aXRsZTogIk5hbm9zdHJpbmcgR2VvTXggYW5hbHlzaXMiDQphdXRob3I6ICJJZXMgTmlqbWFuICYgUGltIEtsb29zdGVybWFuIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgIHRvY19kZXB0aDogMw0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UpDQpgYGANCg0KIVtdKGh0dHA6Ly91c2VxLm5sL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDIyLzEyL1VTRVEtbG9nby1zdWJ0aXRsZS5wbmcpDQoNCiMgS2xhbnQ6IFVzZXENCg0KIyBQcm9qZWN0OiBTcGF0aWFsIE9yZ2FuIEF0bGFzDQoNCiMgRGF0YXNldDogS2lkbmV5DQoNCioqZGF0ZTogYHIgZm9ybWF0KFN5cy50aW1lKCksICclSCAlTSAlYSAlZCAlQiwgJVknKWAqKg0KDQohW10oaHR0cDovL3VzZXEubmwvd3AtY29udGVudC91cGxvYWRzLzIwMjIvMTIvZGVjb3JhdGlvbi1zdHJva2UtZmxhdC5wbmcpDQoNCioqbG9hZGluZyBkZXBlbmRlbmNpZXMqKiBQbGVhc2UgbWFrZSBzdXJlIHRoZSBmb2xsb3dpbmcgcGFja2FnZXMgYXJlDQppbnN0YWxsZWQgYW5kIHJlcXVpcmVkIGxpYnJhcmllcyBjYW4gYmUgbG9hZGVkOg0KDQotICAgaW5zdGFsbC5wYWNrYWdlcygicGtnYnVpbGQiKSAvLyBwa2didWlsZDo6Y2hlY2tfYnVpbGRfdG9vbHMoKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikNCi0gICBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIk5hbm9zdHJpbmctQmlvc3RhdHMvTmFub1N0cmluZ05DVG9vbHMiKQ0KLSAgIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiTmFub3N0cmluZy1CaW9zdGF0cy9HZW9teFRvb2xzIiwgcmVmID0NCiAgICAiZGV2IikNCi0gICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiR2VvTXhXb3JrZmxvd3MiKQ0KLSAgIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiRGF2aXNMYWJvcmF0b3J5L3N0YW5kUiIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIlNwYXRpYWxEZWNvbiIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIkdTVkEiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQotICAgaW5zdGFsbC5wYWNrYWdlcygiRFQiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoIm1zaWdkYnIiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoImRpZ2VzdCIpDQotICAgaW5zdGFsbC5wYWNrYWdlcygicm1hcmtkb3duIikNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCJrYWJsZSIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIkVCSW1hZ2UiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoJ3NjYXR0ZXJtb3JlJykNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCdwYmFwcGx5JykNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCdwbG90cml4JykNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCdnZ3RleHQnKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoJ1JCaW9Gb3JtYXRzJykNCi0gICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiYW9sZXMvUkJpb0Zvcm1hdHMiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoIkM6L1VzZXJzL2luaWptYW4vRG93bmxvYWRzL1NwYXRpYWxPbWljc092ZXJsYXktMC45OS4xMy1iZXRhLnRhci5neiIsDQogICAgZGVwZW5kZW5jaWVzID0gVFJVRSwgcmVwb3MgPSBOVUxMKQ0KLSAgIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiRGF2aXNMYWJvcmF0b3J5L3N0YW5kUiIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoImNsdXN0ZXJQcm9maWxlciIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoInBhdGh2aWV3IikNCg0KYGBge3IgbG9hZF9saWJyYXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCiNsb2FkIGxpYnJhcmllcw0KDQojc3RhY2tfc2l6ZSA8LSBnZXRPcHRpb24oInBhbmRvYy5zdGFjay5zaXplIiwgZGVmYXVsdCA9ICI1MTJtIikNCiNvcHRpb25zKGphdmEucGFyYW1ldGVycyA9IGMoIi1YWDorVXNlQ29uY01hcmtTd2VlcEdDIiwgIi1YbXg4MTkybSwgLVhYOk1ldGFzcGFjZVNpemU9MTAyNE0iKSkNCm9wdGlvbnMoamF2YS5wYXJhbWV0ZXJzID0gYygiLVhYOitVc2VDb25jTWFya1N3ZWVwR0MiLCAiLVhteDgxOTJtIikpDQpsaWJyYXJ5KE5hbm9TdHJpbmdOQ1Rvb2xzKQ0KbGlicmFyeShHZW9teFRvb2xzKQ0KbGlicmFyeShHZW9NeFdvcmtmbG93cykNCmxpYnJhcnkoU3BhdGlhbERlY29uKQ0KbGlicmFyeShHU1ZBKSAjZm9yIHBhdGh3YXkgYW5hbHlzZXMNCmxpYnJhcnkobXNpZ2RicikgI2ZvciBtb2xlY3VsYXIgc2lnbmF0dXJlcyBpbiBwYXRod2F5IGFuYWx5c2VzDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dmb3JjZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoc2NhbGVzKSAjIGZvciBwZXJjZW50DQpsaWJyYXJ5KHJlc2hhcGUyKSAgIyBmb3IgbWVsdA0KbGlicmFyeShjb3dwbG90KSAgICMgZm9yIHBsb3RfZ3JpZA0KbGlicmFyeSh1bWFwKQ0KbGlicmFyeShSdHNuZSkNCmxpYnJhcnkocGhlYXRtYXApICAjIGZvciBwaGVhdG1hcA0KbGlicmFyeShnZ3JlcGVsKSANCmxpYnJhcnkoc2NhbGVzKSAjZm9yIGdncGxvdCBwZWF1ZG9sb2cgdG8gcHJldmVudCBlcnJvcnMgb24gbG9nKDApDQpsaWJyYXJ5KERUKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KbGlicmFyeShTcGF0aWFsT21pY3NPdmVybGF5KQ0KbGlicmFyeShndCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpDQpsaWJyYXJ5KGZnc2VhKQ0KbGlicmFyeShwYXRodmlldykNCmxpYnJhcnkocG5nKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KHZpcmlkaXMpDQoNCmxpYnJhcnkoc3RhbmRSKQ0KbGlicmFyeShTcGF0aWFsRXhwZXJpbWVudCkNCmxpYnJhcnkobGltbWEpDQpsaWJyYXJ5KGdnYWxsdXZpYWwpDQpsaWJyYXJ5KHNjYXRlcikNCg0KI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJzdmEiKQ0KbGlicmFyeShzdmEpDQoNCiNCaW9jTWFuYWdlcjo6aW5zdGFsbCgicHJlcHJvY2Vzc0NvcmUiKSAjcXVhbnRpbGUgbm9ybQ0KbGlicmFyeShwcmVwcm9jZXNzQ29yZSkNCg0KI2luc3RhbGwucGFja2FnZXMoIlBvbHljaHJvbWUiKSAjR2V0IGNvbG9ycw0KbGlicmFyeShQb2x5Y2hyb21lKQ0KDQoNCmxpYnJhcnkoaW5mZXJjbnYpICMgQ05WDQpsaWJyYXJ5KCJhcGUiKQ0KbGlicmFyeSgiQmlvc3RyaW5ncyIpDQpsaWJyYXJ5KCJnZ3RyZWUiKQ0KbGlicmFyeSh0aWR5dHJlZSkNCmBgYA0KDQojIDEgbG9hZGluZyBiYXNlIGZpbGVzDQoNCmBgYHtyIGxvYWRpbmdfYmFzZV9kYXRhfQ0KIyBSZWZlcmVuY2UgdGhlIG1haW4gZm9sZGVyICdmaWxlLnBhdGgnIGNvbnRhaW5pbmcgdGhlIHN1Yi1mb2xkZXJzIHdpdGggZWFjaCBkYXRhIGZpbGUgdHlwZToNCmRhdGFkaXI8LWZpbGUucGF0aCgiTDovcGtsb29zdGVybWFuL0dpdGh1Yi9ES0RfS2lkbmV5LyIpDQpgYGANCg0KVG8gbG9jYXRlIGEgc3BlY2lmaWMgZmlsZSBwYXRoIHJlcGxhY2UgdGhlIGFib3ZlIGxpbmUgd2l0aCBkYXRhZGlyIFw8LQ0KZmlsZS5wYXRoKCJcfi9Gb2xkZXIvU3ViRm9sZGVyL0RhdGFMb2NhdGlvbiIpIHJlcGxhY2UgdGhlIEZvbGRlciwNClN1YkZvbGRlciwgRGF0YUxvY2F0aW9uIGFzIG5lZWRlZC4gVGhlIERhdGFMb2NhdGlvbiBmb2xkZXIgc2hvdWxkDQpjb250YWluIGEgZGNjcywgcGtjcywgYW5kIGFubm90YXRpb24gZm9sZGVyIHdpdGggZWFjaCBzZXQgb2YgZmlsZXMNCnByZXNlbnQgYXMgbmVlZGVkIGF1dG9tYXRpY2FsbHkgbGlzdCBmaWxlcyBpbiBlYWNoIGRpcmVjdG9yeSBmb3IgdXNlLg0KDQoqKlRha2UgY2FyZSB5b3UgaW1wb3J0IGEgY29sdW1uIHdpdGggbnVjbGVpIGNvdW50IHNlcGFyYXRlbHkgaWYgeW91DQp3YW50LioqDQoNCmBgYHtyIHBhcnNlX2ZpbGVzfQ0KRENDRmlsZXMgPC0gZGlyKGZpbGUucGF0aChkYXRhZGlyLCAiZGNjcyIpLCBwYXR0ZXJuID0gIi5kY2MkIiwNCiAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSwgcmVjdXJzaXZlID0gVFJVRSkNClBLQ0ZpbGVzIDwtIGRpcihmaWxlLnBhdGgoZGF0YWRpciwgInBrY3MiKSwgcGF0dGVybiA9ICIucGtjJCIsDQogICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFRSVUUsIHJlY3Vyc2l2ZSA9IFRSVUUpDQpTYW1wbGVBbm5vdGF0aW9uRmlsZSA8LQ0KICBkaXIoZmlsZS5wYXRoKGRhdGFkaXIsICJhbm5vdGF0aW9uIiksIHBhdHRlcm4gPSAiXltefl0iLA0KICAgICAgZnVsbC5uYW1lcyA9IFRSVUUsIHJlY3Vyc2l2ZSA9IFRSVUUpDQpgYGANCg0KIyAyIGxvYWQgZGF0YQ0KDQpgYGB7ciBsb2FkX2RhdGF9DQpEYXRhIDwtDQogIHJlYWROYW5vU3RyaW5nR2VvTXhTZXQoZGNjRmlsZXMgPSBEQ0NGaWxlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICBwa2NGaWxlcyA9IFBLQ0ZpbGVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YUZpbGUgPSBTYW1wbGVBbm5vdGF0aW9uRmlsZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBwaGVub0RhdGFTaGVldCA9ICJUZW1wbGF0ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgcGhlbm9EYXRhRGNjQ29sTmFtZSA9ICJTYW1wbGVfSUQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHByb3RvY29sRGF0YUNvbE5hbWVzID0gYygiYW9pIiwgInJvaSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGV4cGVyaW1lbnREYXRhQ29sTmFtZXMgPSBjKCJwYW5lbCIpKQ0KDQojc2F2ZSBkYXRhIHRvIHByZXZlbnQgbG9hZGluZyB0aW1lIGZvciByZXRha2VzDQojc2F2ZURhdGE8LURhdGENCiNEYXRhPC1zYXZlRGF0YQ0KDQojY2hhbmdlIERhdGEgY29sdW1uIG5hbWVzIGFuZCBtYW51YWwgY29ycmVjdGlvbiBvZiBzcGVsbGluZyBlcnJvcnMNCkRhdGFAcGhlbm9EYXRhQGRhdGFbWyJzbGlkZV9uYW1lIl1dPC1EYXRhQHBoZW5vRGF0YUBkYXRhW1sic2xpZGUgbmFtZSJdXQ0KRGF0YUBwaGVub0RhdGFAZGF0YVtbInNsaWRlIG5hbWUiXV08LSAgTlVMTA0KDQoNCiMrMSByZWZlcmVuY2VzIHRoZSBzbGlkZSBuYW1lIGNvbHVtbg0KYW5uX3NpemU8LWxlbmd0aChjb2xuYW1lcyhEYXRhQHBoZW5vRGF0YUBkYXRhKVtncmVwbCgiQU5OIixjb2xuYW1lcyhEYXRhQHBoZW5vRGF0YUBkYXRhKSldKSsxIA0KYW5uX25hbWVzPC1jKGNvbG5hbWVzKERhdGFAcGhlbm9EYXRhQGRhdGEpW2dyZXBsKCJBTk4iLGNvbG5hbWVzKERhdGFAcGhlbm9EYXRhQGRhdGEpKV0sInNsaWRlX25hbWUiKQ0KDQojIEZlZWwgZnJlZSB0byBjaGFuZ2UgdGhlIG9yZGVyIG9mIHdoaWNoIGNvbG9ycyBhcmUgYXBwb2ludGVkLg0KY29sb3I8LWMoIiNBMzQ5QTQiLCAiI0ZGRkYzMyIsICIjRTcyOThBIiwgIiMwOTE4MzMiLCAiIzFCOUU3NyIsICIjRDk1RjAyIiwgIiM3NTcwQjMiLCAgIiM2NkE2MUUiLCAiI0U2QUIwMiIsICIjOEREM0M3IiwgIiM5RjAwMEYiLCAiI0JFQkFEQSIsICIjRkI4MDcyIiwgIiM4MEIxRDMiLCAiI0ZEQjQ2MiIsICIjQjNERTY5IiwgIiNGQ0NERTUiLCAiI0Q5RDlEOSIsICIjQkM4MEJEIiwgIiNDQ0VCQzUiLCAiI0ZGRUQ2RiIsICIjMzc3RUI4IiwgIiM5ODRFQTMiLCAiIzREQUY0QSIsICIjRkY3MUNFIiwgIiNGRjdGMDAiLCAiI0E2Q0VFMyIsICIjMUY3OEI0IiwgIiNCMkRGOEEiLCAiIzMzQTAyQyIsICIjRkI5QTk5IiwgIiNFMzFBMUMiLCAiI0ZEQkY2RiIsICIjQ0FCMkQ2IiwgIiM2QTNEOUEiLCAiI0ZGRkY5OSIsICIjQjE1OTI4IikNCg0KIyBVc2UgY291bnQgYW5kIGNvdW50X21heCB0byBzZXQgdGhlIHJhbmdlIG9mIGNvbG9yIG5lZWRlZCBmb3IgZWFjaCBjb2x1bW4uDQojIFdpdGggc2V0TmFtZXMoKSB0aGUgY29sb3IgY29kZSBpcyBsaW5rZWQgdG8gZWFjaCB1bmlxdWUgaW5kaXZpZHVhbCB2YWx1ZSBvZiBlYWNoIGNvbHVtbi4NCmNvdW50PTENCmNvbG9yX2xpc3QgPSBsaXN0KCkNCmZvciAoYW5uIGluIGFubl9uYW1lcykgew0KICBjb3VudF9tYXggPSBjb3VudCtsZW5ndGgodW5pcXVlKERhdGFAcGhlbm9EYXRhQGRhdGFbW2Fubl1dKSktMQ0KICBjb2xvcl9saXN0W1thbm5dXTwtc2V0TmFtZXMoY29sb3JbY291bnQ6Y291bnRfbWF4XSwgdW5pcXVlKERhdGFAcGhlbm9EYXRhQGRhdGFbW2Fubl1dKSkNCiAgY291bnQ9Y291bnRfbWF4KzENCn0NCmBgYA0KDQpgYGB7ciBjb2xvciB0YWJsZX0NCnZhbHVlX25hbWVzID0gYygpDQpmb3IgKGFubiBpbiBhbm5fbmFtZXMpIHsNCiAgdmFsdWVfbmFtZXMgPC0gYyh2YWx1ZV9uYW1lcywgdW5pcXVlKERhdGFAcGhlbm9EYXRhQGRhdGFbW2Fubl1dKSkNCn0NCg0KY29sb3JfZGYgPC0gYXMuZGF0YS5mcmFtZSh2YWx1ZV9uYW1lcykNCmNvbG9yX2RmJGNvbG9yIDwtIGNvbG9yWzA6bGVuZ3RoKHZhbHVlX25hbWVzKV0NCg0KY29sb3JfZGYgJT4lIA0KICBtdXRhdGUoY29sb3IgPSBmY3RfaW5vcmRlcihjb2xvcikpIHw+IA0KICBndCgpICU+JSANCiAgZGF0YV9jb2xvcihjb2x1bW5zID0gY29sb3IsIGNvbG9ycyA9IGFzLmNoYXJhY3Rlcihjb2xvcikpICU+JQ0KICB0YWJfb3B0aW9ucyhjb250YWluZXIuaGVpZ2h0ID0gNTAwKQ0KYGBgDQoNCmBgYHtyfQ0KcGFzdGUoIlJlYWRzIGZyb20gZm9sbG93aW5nIHJ1bnMgdXNlZDogIix1bmlxdWUocERhdGEocHJvdG9jb2xEYXRhKERhdGEpKSRTZXFTZXRJZCkpDQpgYGANCg0KIyAzIFN0dWR5IGRlc2lnbg0KDQpgYGB7ciBhbm5vdGF0ZX0NCnBrY3MgPC0gYW5ub3RhdGlvbihEYXRhKQ0KbW9kdWxlcyA8LSBnc3ViKCIucGtjIiwgIiIsIHBrY3MpDQprYWJsZShkYXRhLmZyYW1lKFBLQ3MgPSBwa2NzLCBtb2R1bGVzID0gbW9kdWxlcykpDQpgYGANCg0KU2VsZWN0IHRoZSBhbm5vdGF0aW9ucyB3ZSB3YW50IHRvIHNob3csIHVzZSBcYFxgIHRvIHN1cnJvdW5kIGNvbHVtbg0KbmFtZXMgd2l0aCBzcGFjZXMgb3Igc3BlY2lhbCBzeW1ib2xzDQoNCmBgYHtyIHNlbGVjdF9hbm5vdGF0aW9uc30NCiNjb3VudF9tYXQgPC0gZHBseXI6OmNvdW50KHBEYXRhKERhdGEpLCBBTk4xLEFOTjIsc2xpZGVfbmFtZSkNCmNvdW50X21hdCA8LSBwRGF0YShEYXRhKSAlPiUgZHBseXI6OmNvdW50KHBEYXRhKERhdGEpW2MoYW5uX25hbWVzKV0pDQpgYGANCg0KU2ltcGxpZnkgdGhlIHNsaWRlIG5hbWVzIGlmIHJlcXVpcmVkDQoNCmBgYHtyIHNpbXBsaWZ5X25hbWVzfQ0KIyBjb3VudF9tYXQkc2xpZGVfbmFtZSA8LSBnc3ViKCJkaXNlYXNlIiwgImQiLCBnc3ViKCJub3JtYWwiLCAibiIsIGNvdW50X21hdCRzbGlkZV9uYW1lKSkNCiNjb3VudF9tYXQkcGF0aF9hbm4gPC0gZ3N1YigiaSIsICIiLCBjb3VudF9tYXQkcGF0aF9hbm4pICNjb3JyZWN0aW5nIHNwZWxsaW5nIGVycm9yDQpgYGANCg0KR2F0aGVyIHRoZSBkYXRhIGFuZCBwbG90IGluIG9yZGVyOiBjbGFzcywgc2xpZGUgbmFtZSwgcmVnaW9uLCBzZWdtZW50DQoNCmBgYHtyIGdhdGhlcl9kYXRhfQ0KdGVzdF9nciA8LSBnYXRoZXJfc2V0X2RhdGEoY291bnRfbWF0LCAxOmFsbF9vZihhbm5fc2l6ZSkpDQp0ZXN0X2dyJHggPC0gZmFjdG9yKHRlc3RfZ3IkeCwgbGFiZWxzID0gYW5uX25hbWVzKQ0KYGBgDQoNClBsb3QgU2Fua2V5DQoNCmBgYHtyIFNhbmtleV9wbG90LCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMX0NCmdncGxvdCh0ZXN0X2dyLCBoZWlnaHQgPSAxMCwgd2lkdGggPSAxMCwgYWVzKHgsIGlkID0gaWQsIHNwbGl0ID0geSwgdmFsdWUgPSBuKSkgKw0KICBnZW9tX3BhcmFsbGVsX3NldHMoYWVzKGZpbGwgPSBBTk4yKSwgYWxwaGEgPSAwLjUsIGF4aXMud2lkdGggPSAwLjEpICsNCiAgZ2VvbV9wYXJhbGxlbF9zZXRzX2F4ZXMoYXhpcy53aWR0aCA9IDAuMikgKw0KICBnZW9tX3BhcmFsbGVsX3NldHNfbGFiZWxzKGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDUpICsNCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMikgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKDApKSArIA0KICBzY2FsZV94X2Rpc2NyZXRlKGV4cGFuZCA9IGV4cGFuc2lvbigwKSkgKw0KICBsYWJzKHggPSAiIiwgeSA9ICIiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jb2xvcl9saXN0JEFOTjIpICsNCiAgYW5ub3RhdGUoZ2VvbSA9ICJzZWdtZW50IiwgeCA9IDQuMjUsIHhlbmQgPSA0LjI1LA0KICAgICAgICAgICB5ID0gMTAsIHllbmQgPSA2MSwgbHdkID0gMikgKw0KICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCB4ID0gNC4xOSwgeSA9IDI1LCBhbmdsZSA9IDkwLCBzaXplID0gNSwNCiAgICAgICAgICAgaGp1c3QgPSAwLjUsIGxhYmVsID0gIjUwIHNlZ21lbnRzIikNCmBgYA0KDQojIDQgUUMgJiBQcmUtcHJvY2Vzc2luZw0KDQpTaGlmdCBjb3VudHMgdG8gb25lDQoNCmBgYHtyIHNoaWZ0X2NvdW50c30NCiNzaGlmdCBhbnkgZXhwcmVzc2lvbiBjb3VudHMgd2l0aCBhIHZhbHVlIG9mIDAgdG8gMSB0byBlbmFibGUgaW4gZG93bnN0cmVhbSB0cmFuc2Zvcm1hdGlvbnMuDQpEYXRhIDwtIHNoaWZ0Q291bnRzT25lKERhdGEsIHVzZURBTG9naWMgPSBUUlVFKQ0KYGBgDQoNCiMgNC4xIFNlZ21lbnQgUUMNCg0KV2UgZmlyc3QgYXNzZXNzIHNlcXVlbmNpbmcgcXVhbGl0eSBhbmQgYWRlcXVhdGUgdGlzc3VlIHNhbXBsaW5nIGZvcg0KZXZlcnkgUk9JL0FPSSBzZWdtZW50Lg0KDQpFdmVyeSBST0kvQU9JIHNlZ21lbnQgd2lsbCBiZSB0ZXN0ZWQgZm9yOg0KDQpSYXcgc2VxdWVuY2luZyByZWFkczogc2VnbWVudHMgd2l0aCBcPjEwMDAgcmF3IHJlYWRzIGFyZSByZW1vdmVkLiAlDQpBbGlnbmVkLCUgVHJpbW1lZCwgb3IgJSBTdGl0Y2hlZCBzZXF1ZW5jaW5nIHJlYWRzOiBzZWdtZW50cyBiZWxvdyBcfjgwJQ0KZm9yIG9uZSBvciBtb3JlIG9mIHRoZXNlIFFDIHBhcmFtZXRlcnMgYXJlIHJlbW92ZWQuICUgU2VxdWVuY2luZw0Kc2F0dXJhdGlvbiAoWzEtZGVkdXBsaWNhdGVkIHJlYWRzL2FsaWduZWQgcmVhZHNdJSk6IHNlZ21lbnRzIGJlbG93IFx+NTAlDQpyZXF1aXJlIGFkZGl0aW9uYWwgc2VxdWVuY2luZyB0byBjYXB0dXJlIGZ1bGwgc2FtcGxlIGRpdmVyc2l0eSBhbmQgYXJlDQpub3QgdHlwaWNhbGx5IGFuYWx5emVkIHVudGlsIGltcHJvdmVkLiBOZWdhdGl2ZSBDb3VudDogdGhpcyBpcyB0aGUNCmdlb21ldHJpYyBtZWFuIG9mIHRoZSBzZXZlcmFsIHVuaXF1ZSBuZWdhdGl2ZSBwcm9iZXMgaW4gdGhlIEdlb014IHBhbmVsDQp0aGF0IGRvIG5vdCB0YXJnZXQgbVJOQSBhbmQgZXN0YWJsaXNoIHRoZSBiYWNrZ3JvdW5kIGNvdW50IGxldmVsIHBlcg0Kc2VnbWVudDsgc2VnbWVudHMgd2l0aCBsb3cgbmVnYXRpdmUgY291bnRzICgxLTEwKSBhcmUgbm90IG5lY2Vzc2FyaWx5DQpyZW1vdmVkIGJ1dCBtYXkgYmUgc3R1ZGllZCBjbG9zZXIgZm9yIGxvdyBlbmRvZ2Vub3VzIGdlbmUgc2lnbmFsIGFuZC9vcg0KaW5zdWZmaWNpZW50IHRpc3N1ZSBzYW1wbGluZy4gTm8gVGVtcGxhdGUgQ29udHJvbCAoTlRDKSBjb3VudDogdmFsdWVzDQpcPjEsMDAwIGNvdWxkIGluZGljYXRlIGNvbnRhbWluYXRpb24gZm9yIHRoZSBzZWdtZW50cyBhc3NvY2lhdGVkIHdpdGgNCnRoaXMgTlRDOyBob3dldmVyLCBpbiBjYXNlcyB3aGVyZSB0aGUgTlRDIGNvdW50IGlzIGJldHdlZW4gMSwwMDAtDQoxMCwwMDAsIHRoZSBzZWdtZW50cyBtYXkgYmUgdXNlZCBpZiB0aGUgTlRDIGRhdGEgaXMgdW5pZm9ybWx5IGxvdyAoZS5nLg0KMC0yIGNvdW50cyBmb3IgYWxsIHByb2JlcykuIE51Y2xlaTogXD4xMDAgbnVjbGVpIHBlciBzZWdtZW50IGlzDQpnZW5lcmFsbHkgcmVjb21tZW5kZWQ7IGhvd2V2ZXIsIHRoaXMgY3V0b2ZmIGlzIGhpZ2hseSBzdHVkeS90aXNzdWUNCmRlcGVuZGVudCBhbmQgbWF5IG5lZWQgdG8gYmUgcmVkdWNlZDsgd2hhdCBpcyBtb3N0IGltcG9ydGFudCBpcw0KY29uc2lzdGVuY3kgaW4gdGhlIG51Y2xlaSBkaXN0cmlidXRpb24gZm9yIHNlZ21lbnRzIHdpdGhpbiB0aGUgc3R1ZHkuDQpBcmVhOiBnZW5lcmFsbHkgY29ycmVsYXRlcyB3aXRoIG51Y2xlaTsgYSBzdHJpY3QgY3V0b2ZmIGlzIG5vdCBnZW5lcmFsbHkNCmFwcGxpZWQgYmFzZWQgb24gYXJlYS4NCg0KIyA0LjEuMSBTZWxlY3QgU2VnbWVudCBRQw0KDQpGaXJzdCwgd2Ugc2VsZWN0IHRoZSBRQyBwYXJhbWV0ZXIgY3V0b2ZmcywgYWdhaW5zdCB3aGljaCBvdXIgUk9JL0FPSQ0Kc2VnbWVudHMgd2lsbCBiZSB0ZXN0ZWQgYW5kIGZsYWdnZWQgYXBwcm9wcmlhdGVseS4gV2UgaGF2ZSBzZWxlY3RlZCB0aGUNCmFwcHJvcHJpYXRlIHN0dWR5LXNwZWNpZmljIHBhcmFtZXRlcnMgZm9yIHRoaXMgc3R1ZHkuIE5vdGU6IHRoZSBkZWZhdWx0DQpRQyB2YWx1ZXMgcmVjb21tZW5kZWQgYWJvdmUgYXJlIGFkdmlzZWQgd2hlbiBzdXJ2ZXlpbmcgYSBuZXcgZGF0YXNldCBmb3INCnRoZSBmaXJzdCB0aW1lLg0KDQpEZWZhdWx0IFFDIGN1dG9mZnMgYXJlIGNvbW1lbnRlZCBpbiAoKSBhZGphY2VudCB0byB0aGUgcmVzcGVjdGl2ZQ0KcGFyYW1ldGVycyBzdHVkeS1zcGVjaWZpYyB2YWx1ZXMgd2VyZSBzZWxlY3RlZCBhZnRlciB2aXN1YWxpemluZyB0aGUgUUMNCnJlc3VsdHMgaW4gbW9yZSBkZXRhaWwgYmVsb3cNCg0KYGBge3Igc2V0X1FDX3BhcmFtc30NClFDX3BhcmFtcyA8LQ0KICBsaXN0KG1pblNlZ21lbnRSZWFkcyA9IDEwMDAsICMgTWluaW11bSBudW1iZXIgb2YgcmVhZHMgKDEwMDApDQogICAgICAgcGVyY2VudFRyaW1tZWQgPSA4MCwgICAgIyBNaW5pbXVtICUgb2YgcmVhZHMgdHJpbW1lZCAoODAlKQ0KICAgICAgIHBlcmNlbnRTdGl0Y2hlZCA9IDgwLCAgICMgTWluaW11bSAlIG9mIHJlYWRzIHN0aXRjaGVkICg4MCUpDQogICAgICAgcGVyY2VudEFsaWduZWQgPSA3NSwgICAgIyBNaW5pbXVtICUgb2YgcmVhZHMgYWxpZ25lZCAoODAlKQ0KICAgICAgIHBlcmNlbnRTYXR1cmF0aW9uID0gNTAsICMgTWluaW11bSBzZXF1ZW5jaW5nIHNhdHVyYXRpb24gKDUwJSkNCiAgICAgICBtaW5OZWdhdGl2ZUNvdW50ID0gMSwgICAjIE1pbmltdW0gbmVnYXRpdmUgY29udHJvbCBjb3VudHMgKDEwKQ0KICAgICAgIG1heE5UQ0NvdW50ID0gOTAwMCwgICAgICMgTWF4aW11bSBjb3VudHMgb2JzZXJ2ZWQgaW4gTlRDIHdlbGwgKDEwMDApDQogICAgICAgbWluTnVjbGVpID0gMjAsICAgICAgICAjIE1pbmltdW0gIyBvZiBudWNsZWkgZXN0aW1hdGVkICgxMDApDQogICAgICAgbWluQXJlYSA9IDEwMDApICAgICAgICAgIyBNaW5pbXVtIHNlZ21lbnQgYXJlYSAoNTAwMCkNCkRhdGEgPC0NCiAgc2V0U2VnbWVudFFDRmxhZ3MoRGF0YSwgcWNDdXRvZmZzID0gUUNfcGFyYW1zKSAgICAgICAgDQoNCmNhdCgicHJlLVFDIGZlYXR1cmVzOiIsIGRpbShEYXRhKVsxXSwgIlxucHJlLVFDIHNhbXBsZXM6IiwgZGltKERhdGEpWzJdKQ0KDQojVGFibGUgZm9yIGNsYXJpZmljYXRpb24NClFDcGFyYW1zX2RmIDwtIGRhdGEuZnJhbWUgKA0KICBpdGVtcyA9IGMoIm1pblNlZ21lbnRSZWFkcyIsInBlcmNlbnRUcmltbWVkIiwicGVyY2VudFN0aXRjaGVkIiwicGVyY2VudEFsaWduZWQiLCJwZXJjZW50U2F0dXJhdGlvbiIsDQogICAgICAgICAgICAibWluTmVnYXRpdmVDb3VudCIsIm1heE5UQ0NvdW50IiwibWluTnVjbGVpIiwibWluQXJlYSIpLA0KICBkZWZhdWx0cyA9IGMoMTAwMCw4MCw4MCw4MCw1MCwxMCwxMDAwLDEwMCw1MDAwKSwNCiAgYWN0dWFsID0gYyhRQ19wYXJhbXMkbWluU2VnbWVudFJlYWRzLFFDX3BhcmFtcyRwZXJjZW50VHJpbW1lZCxRQ19wYXJhbXMkcGVyY2VudFN0aXRjaGVkLFFDX3BhcmFtcyRwZXJjZW50QWxpZ25lZCxRQ19wYXJhbXMkcGVyY2VudFNhdHVyYXRpb24sUUNfcGFyYW1zJG1pbk5lZ2F0aXZlQ291bnQsUUNfcGFyYW1zJG1heE5UQ0NvdW50LFFDX3BhcmFtcyRtaW5OdWNsZWksUUNfcGFyYW1zJG1pbkFyZWEpDQopDQoNCmRhdGF0YWJsZShRQ3BhcmFtc19kZiwgcm93bmFtZXM9RkFMU0UsDQogICAgICAgICAgY2FwdGlvbiA9ICJRQyB0aHJlc2hvbGRzIiwNCiAgICAgICAgICBleHRlbnNpb25zID0gJ0J1dHRvbnMnLCBvcHRpb25zID0gbGlzdCAoDQogICAgICAgICAgICBkb20gPSAnQmZ0cmlwJywNCiAgICAgICAgICAgIGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKQ0KICAgICAgICAgICkNCikNCg0KYGBgDQoNCkNvbGxhdGUgUUMgUmVzdWx0cw0KDQpgYGB7ciBjb2xsYXRlX1FDX3Jlc3VsdHN9DQpRQ1Jlc3VsdHMgPC0gcHJvdG9jb2xEYXRhKERhdGEpW1siUUNGbGFncyJdXQ0KZmxhZ19jb2x1bW5zIDwtIGNvbG5hbWVzKFFDUmVzdWx0cykNClFDX1N1bW1hcnkgPC0gZGF0YS5mcmFtZShQYXNzID0gY29sU3VtcyghUUNSZXN1bHRzWywgZmxhZ19jb2x1bW5zXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgV2FybmluZyA9IGNvbFN1bXMoUUNSZXN1bHRzWywgZmxhZ19jb2x1bW5zXSkpDQoNClFDUmVzdWx0cyRRQ1N0YXR1cyA8LSBhcHBseShRQ1Jlc3VsdHMsIDFMLCBmdW5jdGlvbih4KSB7DQogIGlmZWxzZShzdW0oeCkgPT0gMEwsICJQQVNTIiwgIldBUk5JTkciKQ0KfSkNCg0KUUNfU3VtbWFyeVsiVE9UQUwgRkxBR1MiLCBdIDwtDQogIGMoc3VtKFFDUmVzdWx0c1ssICJRQ1N0YXR1cyJdID09ICJQQVNTIiksDQogICAgc3VtKFFDUmVzdWx0c1ssICJRQ1N0YXR1cyJdID09ICJXQVJOSU5HIikpDQoNCmNvbF9ieSA8LSAiQU5OMSINCmNvbF9ieV9wbGF0ZSA8LSAiUGxhdGVfSUQiDQpgYGANCg0KIyA0LjIgR3JhcGhpY2FsIHN1bW1hcmllcyBvZiBRQyBzdGF0aXN0aWNzIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNClVzZSB0aGUgdGFiLW1lbnUgdG8gbmF2aWdhdGUhDQoNCmBgYHtyIFFDX3Bsb3R0aW5nfQ0KUUNfaGlzdG9ncmFtIDwtIGZ1bmN0aW9uKGFzc2F5X2RhdGEgPSBOVUxMLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb24gPSBOVUxMLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGxfYnkgPSBOVUxMLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHRociA9IE5VTEwsDQogICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVfdHJhbnMgPSBOVUxMKSB7DQogIHBsdCA8LSBnZ3Bsb3QoYXNzYXlfZGF0YSwNCiAgICAgICAgICAgICAgICBhZXNfc3RyaW5nKHggPSBwYXN0ZTAoInVubGlzdChgIiwgYW5ub3RhdGlvbiwgImApIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gZmlsbF9ieSkpICsNCiAgICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNTApICsNCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSB0aHIsIGx0eSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIpICsNCiAgICB0aGVtZV9idygpICsgZ3VpZGVzKGZpbGwgPSAibm9uZSIpICsNCiAgICBmYWNldF93cmFwKGFzLmZvcm11bGEocGFzdGUoIn4iLCBmaWxsX2J5KSksIG5yb3cgPSA0KSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNvbG9yX2xpc3QkQU5OMSkgKw0KICAgIGxhYnMoeCA9IGFubm90YXRpb24sIHkgPSAic2VnbWVudHMsICMiLCB0aXRsZSA9IGFubm90YXRpb24pDQogIGlmKCFpcy5udWxsKHNjYWxlX3RyYW5zKSkgew0KICAgIHBsdCA8LSBwbHQgKw0KICAgICAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gc2NhbGVfdHJhbnMpDQogIH0NCiAgcGx0DQp9DQpgYGANCg0KIyMgVHJpbW1lZA0KDQpgYGB7cn0NClFDX2hpc3RvZ3JhbShzRGF0YShEYXRhKSwgIlRyaW1tZWQgKCUpIiwgY29sX2J5LCBRQ19wYXJhbXMkcGVyY2VudFRyaW1tZWQpDQpgYGANCg0KIyMgU3RpY2hlZCAoJSkNCg0KYGBge3J9DQpRQ19oaXN0b2dyYW0oc0RhdGEoRGF0YSksICJTdGl0Y2hlZCAoJSkiLCBjb2xfYnksIFFDX3BhcmFtcyRwZXJjZW50U3RpdGNoZWQpDQpgYGANCg0KIyMgQWxpZ25lZCAoJSkNCg0KYGBge3J9DQpRQ19oaXN0b2dyYW0oc0RhdGEoRGF0YSksICJBbGlnbmVkICglKSIsIGNvbF9ieSxRQ19wYXJhbXMkcGVyY2VudEFsaWduZWQpDQpgYGANCg0KIyMgU2VxdWVuY2luZyBTYXR1cmF0aW9uICglKSB7LmFjdGl2ZX0NCg0KYGBge3J9DQpRQ19oaXN0b2dyYW0oc0RhdGEoRGF0YSksICJTYXR1cmF0ZWQgKCUpIiwgY29sX2J5LCBRQ19wYXJhbXMkcGVyY2VudFNhdHVyYXRpb24pICsNCiAgbGFicyh0aXRsZSA9ICJTZXF1ZW5jaW5nIFNhdHVyYXRpb24gKCUpIiwNCiAgICAgICB4ID0gIlNlcXVlbmNpbmcgU2F0dXJhdGlvbiAoJSkiKQ0KYGBgDQoNCiMjIEFyZWENCg0KYGBge3J9DQpRQ19oaXN0b2dyYW0oc0RhdGEoRGF0YSksICJhcmVhIiwgY29sX2J5LCBRQ19wYXJhbXMkbWluQXJlYSwgc2NhbGVfdHJhbnMgPSAibG9nMTAiKQ0KYGBgDQoNCiMjIE51Y2xlaSBjb3VudA0KDQpgYGB7cn0NClFDX2hpc3RvZ3JhbShzRGF0YShEYXRhKSwgIm51Y2xlaSIsIGNvbF9ieSwgUUNfcGFyYW1zJG1pbk51Y2xlaSkNCmBgYA0KDQojIyBEdXBsaWNhdGlvblJhdGUNCg0KYGBge3J9DQpnZ3Bsb3QocERhdGEocHJvdG9jb2xEYXRhKERhdGEpKSwNCiAgICAgICBhZXMoeCA9IFBsYXRlX0lELCBmaWxsPVBsYXRlX0lELA0KICAgICAgICAgIHkgPSAoRGVkdXBsaWNhdGVkUmVhZHMvUmF3KSkpICsNCiAgZ2VvbV92aW9saW4oKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gLjIpICsNCiAgbGFicyh5ID0gIkRlZHVwbGljYXRlZCAvIFJhdyByZWFkcyIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKw0KICB0aGVtZV9idygpDQpgYGANCg0KIyMgTmVncHJvYmVzIHZzIEVuZG9nZW5vdXMNCg0KYGBge3IgcGxvdF9uZWdwcm9iZV9kYXRhLCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD01fQ0KdG1wX3RhcmdldF9EYXRhIDwtIGFnZ3JlZ2F0ZUNvdW50cyhEYXRhKQ0KDQojZ2V0IG5lZ2F0aXZlIHByb2JlIGRhdGENCm5lZ3M8LXN1YnNldCh0bXBfdGFyZ2V0X0RhdGEsQ29kZUNsYXNzPT0iTmVnYXRpdmUiKQ0KDQpwMTwtZ2dwbG90KHBEYXRhKG5lZ3MpLA0KICAgICAgIGFlcyh4ID0gQU5OMiwgZmlsbCA9IEFOTjIsDQogICAgICAgICAgeSA9IGFzc2F5RGF0YUVsZW1lbnQobmVncywgZWx0ID0gImV4cHJzIikpKSArDQogIGdlb21fdmlvbGluKCkgKw0KICBnZW9tX2ppdHRlcih3aWR0aCA9IC4yKSArDQogIGxhYnMoeSA9ICJOZWdhdGl2ZSBwcm9iZXMgRXhwcmVzc2lvbiIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMSwzMDAwKSwgdHJhbnMgPSAibG9nMiIpICsNCiAgdGhlbWVfYncoKSArIGNvb3JkX2ZsaXAoKQ0KDQoNCiMgZ2V0IGVuZG9nZW5vdXMgcHJvYmUgZGF0YQ0KZW5kPC1zdWJzZXQodG1wX3RhcmdldF9EYXRhLENvZGVDbGFzcz09IkVuZG9nZW5vdXMiKQ0KDQpwMjwtZ2dwbG90KHBEYXRhKGVuZCksDQogICAgICAgYWVzKHggPSBBTk4yLCBmaWxsID0gQU5OMiwNCiAgICAgICAgICAgeSA9IGNvbE1lYW5zKGFzc2F5RGF0YUVsZW1lbnQoZW5kLCBlbHQgPSAiZXhwcnMiKSkpKSArDQogIGdlb21fdmlvbGluKCkgKw0KICBnZW9tX2ppdHRlcih3aWR0aCA9IC4yKSArDQogIGxhYnMoeSA9ICJFbmRvZ2Vub3VzIHByb2JlcyBFeHByZXNzaW9uIChtZWFuKSIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMSwzMDAwKSx0cmFucyA9ICJsb2cyIikgKw0KICB0aGVtZV9idygpICsgY29vcmRfZmxpcCgpDQoNCnBsIDwtbGlzdChwMSxwMikNCnBsb3RfZ3JpZChwbG90bGlzdD1wbCwgbnJvdz0yLCBhbGlnbj0ndicpDQoNCmBgYA0KDQojIyBOZWdfcHJvYmUgcmVhZHMgY29tcGFyZWQgdG8gcmF3X3JlYWRzDQoNCmBgYHtyfQ0KDQojIG1ha2UgYmFja2dyb3VuZCB0b3RhbCBuZWcgcHJvYmUgY291bnQNCmZkYXRhX2RmPC1mRGF0YShEYXRhKQ0KbmVncHJvYmVzbmFtZXM8LXJvd25hbWVzKGZkYXRhX2RmW2ZkYXRhX2RmJE5lZ2F0aXZlPT1UUlVFLF0pDQp0ZW1wX2V4cDwtYXNzYXlEYXRhRWxlbWVudChEYXRhLGVsdD0nZXhwcnMnKQ0KbmVncHJvYmVfZXhwcl9mZDwtdGVtcF9leHBbcm93bmFtZXModGVtcF9leHApICVpbiUgbmVncHJvYmVzbmFtZXMsXQ0KdG90X25lZ19jdHJsX3JlYWRzPC1jb2xTdW1zKG5lZ3Byb2JlX2V4cHJfZmQpDQp0b3RfZGVkdXBfcmVhZHM8LXBEYXRhKHByb3RvY29sRGF0YShEYXRhKSkkRGVkdXBsaWNhdGVkUmVhZHMNCg0KZGY8LWRhdGEuZnJhbWUoJ2FvaSc9IG5hbWVzKHRvdF9uZWdfY3RybF9yZWFkcyksJ3RvdF9kZWR1cF9yZWFkcycgPSBhcy5udW1lcmljKHRvdF9kZWR1cF9yZWFkcyksJ3RvdF9uZWdfY3RybF9yZWFkcyc9YXMubnVtZXJpYyh0b3RfbmVnX2N0cmxfcmVhZHMpKQ0KZGY8LW1lbHQoZGYsaWQ9ImFvaSIpDQpnZ3Bsb3QoZGYsYWVzKGZpbGw9dmFyaWFibGUseT12YWx1ZSx4PWFvaSkpICsgDQogIGdlb21fYmFyKHBvc2l0aW9uPSJpZGVudGl0eSIsc3RhdD0iaWRlbnRpdHkiKSArDQogIHNjYWxlX3lfY29udGludW91cyh0cmFucyA9IGxvZzJfdHJhbnMoKSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIsYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSkgICAgICAgICAgICAgICAgICAgICANCiANCmBgYA0KDQojIyBEdXBsaWNhdGVkIHJlYWRzIHZzIEJhY2tncm91bmQNCg0KYGBge3J9DQojIGdldCBkY2MgcGVyIHBsYXRlLiBzdW0gbmVncHJvYmUgY291bnRzL2RjYy9wbGF0ZQ0KZ2dwbG90KHBEYXRhKHByb3RvY29sRGF0YShEYXRhKSksDQogICAgICAgYWVzKHggPSBQbGF0ZV9JRCwgZmlsbD1QbGF0ZV9JRCwNCiAgICAgICAgICB5ID0gRGVkdXBsaWNhdGVkUmVhZHMpKSArDQogIGdlb21fdmlvbGluKCkgKw0KICBnZW9tX2ppdHRlcih3aWR0aCA9IC4yKSArDQogIGxhYnMoeSA9ICJEZWR1cGxpY2F0ZWQgLyBSYXcgcmVhZHMiKSArDQogIHNjYWxlX3lfbG9nMTAoKSsNCiAgZ2VvbV9obGluZShkYXRhID1wRGF0YShwcm90b2NvbERhdGEoRGF0YSkpICwgDQogICAgICAgICAgIGFlcyh5aW50ZXJjZXB0ID0gTlRDLCBjb2xvdXI9UGxhdGVfSUQpKSArDQogIHRoZW1lX2J3KCkNCg0KYGBgDQoNCiMjIER1cGxpY2F0ZWQgcmVhZHMgdnMgUk9JYXJlYQ0KDQpgYGB7ciwgZmlnLndpZHRoPTE1LGZpZy5oZWlnaHQ9NX0NCnRlbXBfZGY8LWNiaW5kKHBEYXRhKERhdGEpLHBEYXRhKHByb3RvY29sRGF0YShEYXRhKSksZGNjPXJvd25hbWVzKHBEYXRhKERhdGEpKSkNCg0KZ2dwbG90KHRlbXBfZGYsDQogICAgICAgYWVzKHggPSBkY2MsIGNvbG91cj1zbGlkZV9uYW1lLA0KICAgICAgICAgIHkgPSAoRGVkdXBsaWNhdGVkUmVhZHMvYXJlYSkgKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICB5bGltKDAsMjApICsgDQogIGxhYnMoeSA9ICJEZWR1cGxpY2F0ZWQgcmVhZHMgLyBST0kgYXJlYSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9NiwgYW5nbGU9OTAsIGhqdXN0PTEpICkNCg0KYGBgDQoNCiMjIER1cGxpY2F0ZWQgcmVhZHMgdnMgbnVjbGVpDQoNCmBgYHtyLCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD01fQ0KdGVtcF9kZjwtY2JpbmQocERhdGEoRGF0YSkscERhdGEocHJvdG9jb2xEYXRhKERhdGEpKSxkY2M9cm93bmFtZXMocERhdGEoRGF0YSkpKQ0KDQpnZ3Bsb3QodGVtcF9kZiwNCiAgICAgICBhZXMoeCA9IGRjYywgY29sb3VyPXNsaWRlX25hbWUsDQogICAgICAgICAgeSA9IChEZWR1cGxpY2F0ZWRSZWFkcy9udWNsZWkpICkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgeWxpbSgwLDUwKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBBZGp1c3QgcGVyIHByb2plY3QNCiAgbGFicyh5ID0gIkRlZHVwbGljYXRlZCByZWFkcyAvIG51Y2xlaSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9NiwgYW5nbGU9OTAsIGhqdXN0PTEpICkNCg0KYGBgDQoNCiMgNC4zIFByb2Nlc3MgTmVnYXRpdmUgR2VvTWVhbnMNCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgbmVnYXRpdmUgZ2VvbWV0cmljIG1lYW5zIGZvciBlYWNoIG1vZHVsZQ0KIyBJdCB3aWxsIHNob3cgb25seSB0aGUgbmVnYXRpdmUgcHJvYmVzIGdlb21lYW4sIHNvIGV4cGVjdCBsZXNzIHNlZ21lbnRzLg0KbmVnYXRpdmVHZW9NZWFucyA8LSANCiAgZXNCeShuZWdhdGl2ZUNvbnRyb2xTdWJzZXQoRGF0YSksIA0KICAgICAgIEdST1VQID0gIk1vZHVsZSIsIA0KICAgICAgIEZVTiA9IGZ1bmN0aW9uKHgpIHsgDQogICAgICAgICBhc3NheURhdGFBcHBseSh4LCBNQVJHSU4gPSAyLCBGVU4gPSBuZ2VvTWVhbiwgZWx0ID0gImV4cHJzIikgDQogICAgICAgfSkgDQpwcm90b2NvbERhdGEoRGF0YSlbWyJOZWdHZW9NZWFuIl1dIDwtIG5lZ2F0aXZlR2VvTWVhbnMNCg0KbmVnQ29scyA8LSBwYXN0ZTAoIk5lZ0dlb01lYW5fIiwgbW9kdWxlcykNCnBEYXRhKERhdGEpWywgbmVnQ29sc10gPC0gc0RhdGEoRGF0YSlbWyJOZWdHZW9NZWFuIl1dDQpmb3IoYW5uIGluIG5lZ0NvbHMpIHsNCiAgcGx0IDwtIFFDX2hpc3RvZ3JhbShwRGF0YShEYXRhKSwgYW5uLCBjb2xfYnksIDIsIHNjYWxlX3RyYW5zID0gImxvZzEwIikNCiAgcHJpbnQocGx0KQ0KfQ0KDQoNCiMgRGV0YXRjaCBuZWdfZ2VvbWVhbiBjb2x1bW5zIGFoZWFkIG9mIGFnZ3JlZ2F0ZUNvdW50cyBjYWxsDQoNCnBEYXRhKERhdGEpIDwtIHBEYXRhKERhdGEpWywgIWNvbG5hbWVzKHBEYXRhKERhdGEpKSAlaW4lIG5lZ0NvbHNdDQoNCmBgYA0KDQpTaG93IGFsbCBOVEMgdmFsdWVzLCBGcmVxID0gXCMgb2YgU2VnbWVudHMgd2l0aCBhIGdpdmVuIE5UQyBjb3VudDoNCg0KYGBge3IgUUNfdGFibGVzfQ0KUUM8LXNEYXRhKERhdGEpDQoNCm50Y19kZiA8LVFDWyxjKCJzbGlkZV9uYW1lIiwiUGxhdGVfSUQiLCJOVENfSUQiLCJOVEMiKV0NCnRlbXB0YWJsZTwtbnRjX2RmICU+JSBkcGx5cjo6Y291bnQobnRjX2RmJHNsaWRlX25hbWUsbnRjX2RmJE5UQ19JRCxudGNfZGYkUGxhdGVfSUQsbnRjX2RmJE5UQykNCmNvbG5hbWVzKHRlbXB0YWJsZSkgPC0gYygiU2xpZGVfbmFtZSIsIk5UQ19JRCIsIlBsYXRlX0lEIiwiTlRDX2NvdW50IiwiTnVtYmVyX29mX3NhbXBsZXMiKQ0KZGF0YXRhYmxlKHRlbXB0YWJsZSwgcm93bmFtZXMgPSBGQUxTRSkNCg0KDQprYWJsZSh0YWJsZShOVENfQ291bnQgPSBzRGF0YShEYXRhKSROVEMpLCBjb2wubmFtZXMgPSBjKCJOVEMgQ291bnQiLCAiIyBvZiBTZWdtZW50cyIpKQ0KDQprYWJsZShRQ19TdW1tYXJ5LCBjYXB0aW9uID0gIlFDIFN1bW1hcnkgVGFibGUgZm9yIGVhY2ggU2VnbWVudCIpDQoNCmRhdGF0YWJsZShRQ19TdW1tYXJ5LA0KICAgICAgICAgIGNhcHRpb24gPSAiQU9JIFFDIFN1bW1hcnkiLA0KICAgICAgICAgIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsIG9wdGlvbnMgPSBsaXN0ICgNCiAgICAgICAgICAgIGRvbSA9ICdCZnRyaXAnLA0KICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpDQogICAgICAgICAgKQ0KKQ0KYGBgDQoNClNob3cgQU9JcyB3aGljaCBmYWlsIGNyaXRpY2FsIFFDcy4NCg0KYGBge3IgbGlzdF9mYWlsdXJlc30NClFDPC1zRGF0YShEYXRhKQ0KdW5kZXJzYXQ8LXN1YnNldChRQywgYFNhdHVyYXRlZCAoJSlgPD0gUUNfcGFyYW1zJHBlcmNlbnRTYXR1cmF0aW9uKQ0KDQppZihucm93KHVuZGVyc2F0KT4gMCkgew0KDQpkYXRhdGFibGUoYWdncmVnYXRlKHVuZGVyc2F0LCBieT1saXN0KHVuZGVyc2F0JFNhbXBsZUlEKSxwYXN0ZSxjb2xsYXBzZT0iOyIpLA0KICAgICAgICAgIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsIG9wdGlvbnMgPSBsaXN0ICgNCiAgICAgICAgICAgIGRvbSA9ICdCZnRyaXAnLA0KICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpDQogICAgICAgICAgKQ0KKX0NCmBgYA0KDQpTdWJzZXR0aW5nIG91ciBkYXRhc2V0IGhhcyByZW1vdmVkIHNhbXBsZXMgd2hpY2ggZGlkIG5vdCBwYXNzIFFDDQoNCmBgYHtyIHN1YnNldHRpbmdfUUNfZmFpbHN9DQpEYXRhIDwtIERhdGFbLCBRQ1Jlc3VsdHMkUUNTdGF0dXMgPT0gIlBBU1MiXQ0KYGBgDQoNCkdlbmVyYWxseSBrZWVwIHRoZSBxY0N1dG9mZnMgcGFyYW1ldGVycyB1bmNoYW5nZWQuIFNldA0KcmVtb3ZlTG9jYWxPdXRsaWVycyB0byBGQUxTRSBpZiB5b3UgZG8gbm90IHdhbnQgdG8gcmVtb3ZlIGxvY2FsIG91dGxpZXJzDQoNCmBgYHtyIHByb2Nlc3NfUUN9DQpEYXRhIDwtIHNldEJpb1Byb2JlUUNGbGFncyhEYXRhLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxY0N1dG9mZnMgPSBsaXN0KG1pblByb2JlUmF0aW8gPSAwLjEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwZXJjZW50RmFpbEdydWJicyA9IDIwKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVtb3ZlTG9jYWxPdXRsaWVycyA9IEZBTFNFKQ0KDQpQcm9iZVFDUmVzdWx0cyA8LSBmRGF0YShEYXRhKVtbIlFDRmxhZ3MiXV0NCmBgYA0KDQpEZWZpbmUgUUMgdGFibGUgZm9yIFByb2JlIFFDDQoNCmBgYHtyIGRlZmluZV9xY190YWJsZX0NCnFjX2RmIDwtIGRhdGEuZnJhbWUoUGFzc2VkID0gc3VtKHJvd1N1bXMoUHJvYmVRQ1Jlc3VsdHNbLCAtMV0pID09IDApLA0KICAgICAgICAgICAgICAgICAgICBHbG9iYWwgPSBzdW0oUHJvYmVRQ1Jlc3VsdHMkR2xvYmFsR3J1YmJzT3V0bGllciksDQogICAgICAgICAgICAgICAgICAgIExvY2FsID0gc3VtKHJvd1N1bXMoUHJvYmVRQ1Jlc3VsdHNbLCAtMjotMV0pID4gMA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAmICFQcm9iZVFDUmVzdWx0cyRHbG9iYWxHcnViYnNPdXRsaWVyKSkNCmBgYA0KDQpTdWJzZXQgb2JqZWN0IHRvIGV4Y2x1ZGUgYWxsIHRoYXQgZGlkIG5vdCBwYXNzIFJhdGlvICYgR2xvYmFsIHRlc3RpbmcNCg0KYGBge3Igc3Vic2V0fQ0KUHJvYmVRQ1Bhc3NlZCA8LSANCiAgc3Vic2V0KERhdGEsIA0KICAgICAgICAgZkRhdGEoRGF0YSlbWyJRQ0ZsYWdzIl1dWyxjKCJMb3dQcm9iZVJhdGlvIildID09IEZBTFNFICYNCiAgICAgICAgICAgZkRhdGEoRGF0YSlbWyJRQ0ZsYWdzIl1dWyxjKCJHbG9iYWxHcnViYnNPdXRsaWVyIildID09IEZBTFNFKQ0KDQpEYXRhIDwtIFByb2JlUUNQYXNzZWQgDQpjYXQoIkFmdGVyIFFDIGZlYXR1cmVzOiIsIGRpbShEYXRhKVsxXSwgIlxuQWZ0ZXIgUUMgc2FtcGxlczoiLCBkaW0oRGF0YSlbMl0pDQpgYGANCg0KQ2hlY2sgaG93IG1hbnkgdW5pcXVlIHRhcmdldHMgdGhlIG9iamVjdCBoYXMNCg0KYGBge3IgdW5pcXVlX2NoZWNrfQ0KbGVuZ3RoKHVuaXF1ZShmZWF0dXJlRGF0YShEYXRhKVtbIlRhcmdldE5hbWUiXV0pKQ0KYGBgDQoNCkNvbGxhcHNlIHRvIHRhcmdldHMNCg0KYGBge3IgY29sbGFwc190YXJnZXRzfQ0KdGFyZ2V0X0RhdGEgPC0gYWdncmVnYXRlQ291bnRzKERhdGEpDQoNCmV4cHJzKHRhcmdldF9EYXRhKVsxOjUsIDE6Ml0NCmBgYA0KDQpEZWZpbmUgTE9RIFNEIHRocmVzaG9sZCBhbmQgbWluaW11bSB2YWx1ZQ0KDQpgYGB7ciBzZXRfTFNRfQ0KY3V0b2ZmIDwtIDINCm1pbkxPUSA8LSAyDQpgYGANCg0KIyA0LjQgTGltaXQgb2YgUXVhbnRpZmljYXRpb24NCg0KV2UgZGVmaW5lIGEgbGltaXQgb2YgcXVhbnRpZmljYXRpb24gKExPUSkgcGVyIFJPSS9BT0kgc2VnbWVudCBiYXNlZCBvbg0KdGhlIG5lZ2F0aXZlIGNvbnRyb2wgcHJvYmVzIHRvIGd1aWRlIHRoZSBmaWx0ZXJpbmcgb2Ygc2VnbWVudHMgYW5kIGdlbmVzDQp3aXRoIGxvdyBzaWduYWwgcmVsYXRpdmUgdG8gYmFja2dyb3VuZC4gVGhlIGZvcm11bGEgZm9yIGNhbGN1bGF0aW5nIHRoZQ0KTE9RIGluIHRoZSAkaV57dGh9JCBzZWdtZW50IGF0ICRuJCBzdGFuZGFyZCBkZXZpYXRpb25zICgkbiA9IDIkIGZvciB0aGlzDQpzdHVkeSkgaXM6ICRMT1FfaT1nZW9tZWFuKE5lZ1Byb2JlX2kpKmdlb1NEKE5lZ1Byb2JlX2kpXm4kDQoNCkNhbGN1bGF0ZSBMT1EgcGVyIG1vZHVsZSB0ZXN0ZWQNCg0KYGBge3IgY2FsY3VsYXRlX0xPUX0NCkxPUSA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGNvbG5hbWVzKHRhcmdldF9EYXRhKSkNCmZvcihtb2R1bGUgaW4gbW9kdWxlcykgew0KICB2YXJzIDwtIHBhc3RlMChjKCJOZWdHZW9NZWFuXyIsICJOZWdHZW9TRF8iKSwNCiAgICAgICAgICAgICAgICAgbW9kdWxlKQ0KICBpZihhbGwodmFyc1sxOjJdICVpbiUgY29sbmFtZXMocERhdGEodGFyZ2V0X0RhdGEpKSkpIHsNCiAgICBMT1FbLCBtb2R1bGVdIDwtDQogICAgICBwbWF4KG1pbkxPUSwNCiAgICAgICAgICAgcERhdGEodGFyZ2V0X0RhdGEpWywgdmFyc1sxXV0gKiANCiAgICAgICAgICAgICBwRGF0YSh0YXJnZXRfRGF0YSlbLCB2YXJzWzJdXSBeIGN1dG9mZikNCiAgfQ0KfQ0KcERhdGEodGFyZ2V0X0RhdGEpJExPUSA8LSBMT1ENCmBgYA0KDQojIDQuNSBGaWx0ZXJpbmcNCg0KQWZ0ZXIgZGV0ZXJtaW5pbmcgdGhlIGxpbWl0IG9mIHF1YW50aWZpY2F0aW9uIChMT1EpIHBlciBzZWdtZW50LCB3ZQ0KcmVjb21tZW5kIGZpbHRlcmluZyBvdXQgZWl0aGVyIHNlZ21lbnRzIGFuZC9vciBnZW5lcyB3aXRoIGFibm9ybWFsbHkgbG93DQpzaWduYWwuIEZpbHRlcmluZyBpcyBhbiBpbXBvcnRhbnQgc3RlcCB0byBmb2N1cyBvbiB0aGUgdHJ1ZSBiaW9sb2dpY2FsDQpkYXRhIG9mIGludGVyZXN0Lg0KDQpXZSBkZXRlcm1pbmUgdGhlIG51bWJlciBvZiBnZW5lcyBkZXRlY3RlZCBpbiBlYWNoIHNlZ21lbnQgYWNyb3NzIHRoZQ0KZGF0YXNldC4NCg0KYGBge3IgZmlsdGVyaW5nfQ0KTE9RX01hdCA8LSBjKCkNCmZvcihtb2R1bGUgaW4gbW9kdWxlcykgew0KICBpbmQgPC0gZkRhdGEodGFyZ2V0X0RhdGEpJE1vZHVsZSA9PSBtb2R1bGUNCiAgTWF0X2kgPC0gdChlc0FwcGx5KHRhcmdldF9EYXRhW2luZCwgXSwgTUFSR0lOID0gMSwNCiAgICAgICAgICAgICAgICAgICAgIEZVTiA9IGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgICAgICAgICAgICAgICAgeCA+IExPUVssIG1vZHVsZV0NCiAgICAgICAgICAgICAgICAgICAgIH0pKQ0KICBMT1FfTWF0IDwtIHJiaW5kKExPUV9NYXQsIE1hdF9pKQ0KfQ0KIyBlbnN1cmUgb3JkZXJpbmcgc2luY2UgdGhpcyBpcyBzdG9yZWQgb3V0c2lkZSBvZiB0aGUgZ2VvbXhTZXQNCkxPUV9NYXQgPC0gTE9RX01hdFtmRGF0YSh0YXJnZXRfRGF0YSkkVGFyZ2V0TmFtZSwgXQ0KYGBgDQoNCiMgNC41LjEgU2VnbWVudCBHZW5lIERldGVjdGlvbg0KDQpXZSBmaXJzdCBmaWx0ZXIgb3V0IHNlZ21lbnRzIHdpdGggZXhjZXB0aW9uYWxseSBsb3cgc2lnbmFsLiBUaGVzZQ0Kc2VnbWVudHMgd2lsbCBoYXZlIGEgc21hbGwgZnJhY3Rpb24gb2YgcGFuZWwgZ2VuZXMgZGV0ZWN0ZWQgYWJvdmUgdGhlDQpMT1EgcmVsYXRpdmUgdG8gdGhlIG90aGVyIHNlZ21lbnRzIGluIHRoZSBzdHVkeS4gTGV0J3MgdmlzdWFsaXplIHRoZQ0KZGlzdHJpYnV0aW9uIG9mIHNlZ21lbnRzIHdpdGggcmVzcGVjdCB0byB0aGVpciAlIGdlbmVzIGRldGVjdGVkOg0KDQpTYXZlIGRldGVjdGlvbiByYXRlIGluZm9ybWF0aW9uIHRvIHBoZW5vIGRhdGENCg0KYGBge3Igc2F2ZV9kZXRlY3Rpbm9fcmF0ZX0NCnBEYXRhKHRhcmdldF9EYXRhKSRHZW5lc0RldGVjdGVkIDwtIA0KICBjb2xTdW1zKExPUV9NYXQsIG5hLnJtID0gVFJVRSkNCnBEYXRhKHRhcmdldF9EYXRhKSRHZW5lRGV0ZWN0aW9uUmF0ZSA8LQ0KICBwRGF0YSh0YXJnZXRfRGF0YSkkR2VuZXNEZXRlY3RlZCAvIG5yb3codGFyZ2V0X0RhdGEpDQoNCmBgYA0KDQpEZXRlcm1pbmUgZGV0ZWN0aW9uIHRocmVzaG9sZHM6IDElLCA1JSwgMTAlLCAxNSUsIFw+MTUlDQoNCmBgYHtyIGRldGVybWluZSt0aHJlc2hvbGRzfQ0KcERhdGEodGFyZ2V0X0RhdGEpJERldGVjdGlvblRocmVzaG9sZCA8LSANCiAgY3V0KHBEYXRhKHRhcmdldF9EYXRhKSRHZW5lRGV0ZWN0aW9uUmF0ZSwNCiAgICAgIGJyZWFrcyA9IGMoMCwgMC4wMSwgMC4wNSwgMC4xLCAwLjE1LCAwLjIsMSksDQogICAgICBsYWJlbHMgPSBjKCI8MSUiLCAiMS01JSIsICI1LTEwJSIsICIxMC0xNSUiLCAiMTUtMjAlIiwgIj4yMCUiKSkNCg0KIyBzdGFja2VkIGJhciBwbG90IG9mIGRpZmZlcmVudCBjdXQgcG9pbnRzICgxJSwgNSUsIDEwJSwgMTUlKQ0KZ2dwbG90KHBEYXRhKHRhcmdldF9EYXRhKSwNCiAgICAgICBhZXMoeCA9IERldGVjdGlvblRocmVzaG9sZCkpICsNCiAgZ2VvbV9iYXIoYWVzKGZpbGwgPSBBTk4yKSkgKw0KICBnZW9tX3RleHQoc3RhdCA9ICJjb3VudCIsIGFlcyhsYWJlbCA9IC4uY291bnQuLiksIHZqdXN0ID0gLTAuNSkgKw0KICB0aGVtZV9idygpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLCAwLjEpKSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y29sb3JfbGlzdCRBTk4yKSArDQogIGxhYnMoeCA9ICJHZW5lIERldGVjdGlvbiBSYXRlIiwNCiAgICAgICB5ID0gIlNlZ21lbnRzLCAjIiwNCiAgICAgICBmaWxsID0gIlNlZ21lbnQgVHlwZSIpDQpgYGANCg0KY3V0IHBlcmNlbnQgZ2VuZXMgZGV0ZWN0ZWQgYXQgMSwgNSwgMTAsIDE1DQoNCmBgYHtyIGN1dF90b19wZXJjZW50fQ0Ka2FibGUodGFibGUocERhdGEodGFyZ2V0X0RhdGEpJERldGVjdGlvblRocmVzaG9sZCwNCiAgICAgICAgICAgIHBEYXRhKHRhcmdldF9EYXRhKSRBTk4yKSkNCg0KIyBzZXQgdGhyZXNob2xkIGZvciBkZXRlY3Rpb25sZXZlbA0KIyBkZWZhdWx0IDAuMQ0KZ2VuZV9kZXRfdGhyZXNob2xkIDwtIDAuMDUNCg0KdGFyZ2V0X0RhdGEgPC0NCiAgdGFyZ2V0X0RhdGFbLCBwRGF0YSh0YXJnZXRfRGF0YSkkR2VuZURldGVjdGlvblJhdGUgPj0gZ2VuZV9kZXRfdGhyZXNob2xkXQ0KDQpkaW0odGFyZ2V0X0RhdGEpDQpgYGANCg0KIyA0LjYgTWFudWFsIHJlbW92YWwgb2Ygc2FtcGxlcy9jbGFzc2VzDQoNCmBgYHtyIHJlbW92ZV9zYW1wbGVzfQ0KYWN0aXZlX2FvaXM8LW5hbWVzKGFzLmRhdGEuZnJhbWUoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSwgZWx0PSAiZXhwcnMiKSkpDQpgYGANCg0KcmUtQ29sbGVjdCBhbm5vdGF0aW9ucw0KDQpgYGB7ciBjb2xsZWN0X2Fubm90YXRpb25zfQ0KIyBnYXRoZXIgdGhlIGRhdGEgYW5kIHBsb3QgaW4gb3JkZXI6IGNsYXNzLCBzbGlkZSBuYW1lLCByZWdpb24sIHNlZ21lbnQNCiNjb3VudF9tYXQgPC0gZHBseXI6OmNvdW50KHBEYXRhKERhdGEpLCBBTk4xLEFOTjIsQU5OMyxBTk40LHNsaWRlX25hbWUpDQoNCnRlbXBfcWMgPC0gdGVtcF9kZg0KdGVtcF9xYyRRQ1Jlc3VsdCA8LSBRQ1Jlc3VsdHMkUUNTdGF0dXMNCnRlbXBfcWMkUUNSZXN1bHRbdGVtcF9xYyRRQ1Jlc3VsdCA9PSAiV0FSTklORyJdIDwtICJYIg0KDQojY291bnRfbWF0IDwtIGRwbHlyOjpjb3VudChhLCBBTk4xLCBBTk4yLCBzbGlkZV9uYW1lLCBRQ1Jlc3VsdCkNCmNvdW50X21hdCA8LSB0ZW1wX3FjICU+JSBkcGx5cjo6Y291bnQodGVtcF9xY1tjKGFubl9uYW1lcywgIlFDUmVzdWx0IildKQ0KDQp0ZXN0X2dyIDwtIGdhdGhlcl9zZXRfZGF0YShjb3VudF9tYXQsIDE6KGFubl9zaXplKzEpKQ0KdGVzdF9nciR4IDwtIGZhY3Rvcih0ZXN0X2dyJHgsIGxhYmVscyA9IGMoYW5uX25hbWVzLCAiUUNSZXN1bHQiKSkNCmBgYA0KDQpyZS1QbG90IFNhbmtleQ0KDQpgYGB7ciBwbG90X3NhbmtleSwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTF9DQpnZ3Bsb3QodGVzdF9nciwgYWVzKHgsIGlkID0gaWQsIHNwbGl0ID0geSwgdmFsdWUgPSBuKSkgKw0KICBnZW9tX3BhcmFsbGVsX3NldHMoYWVzKGZpbGwgPSBBTk4yKSwgYWxwaGEgPSAwLjUsIGF4aXMud2lkdGggPSAwLjEpICsNCiAgZ2VvbV9wYXJhbGxlbF9zZXRzX2F4ZXMoYXhpcy53aWR0aCA9IDAuMikgKw0KICBnZW9tX3BhcmFsbGVsX3NldHNfbGFiZWxzKGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDUpICsNCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNykgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKDApKSArIA0KICBzY2FsZV94X2Rpc2NyZXRlKGV4cGFuZCA9IGV4cGFuc2lvbigwKSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y29sb3JfbGlzdCRBTk4yKSArDQogIGxhYnMoeCA9ICIiLCB5ID0gIiIpICsNCiAgYW5ub3RhdGUoZ2VvbSA9ICJzZWdtZW50IiwgeCA9IDMuMjUsIHhlbmQgPSAzLjI1LCB5ID0gMTAsIA0KICAgICAgICAgICB5ZW5kID0gNjAsIGx3ZCA9IDIpICsNCiAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgeCA9IDMuMTksIHkgPSAyNSwgYW5nbGUgPSA5MCwgc2l6ZSA9IDUsDQogICAgICAgICAgIGhqdXN0ID0gMC41LCBsYWJlbCA9ICI1MCBzZWdtZW50cyIpDQpgYGANCg0KIyA0LjcgR2VuZSBEZXRlY3Rpb24gUmF0ZQ0KDQpDYWxjdWxhdGUgZGV0ZWN0aW9uIHJhdGUNCg0KYGBge3IgY2xjX2RldGVjdGlvbl9yYXRlfQ0KTE9RX01hdCA8LSBMT1FfTWF0WywgY29sbmFtZXModGFyZ2V0X0RhdGEpXQ0KZkRhdGEodGFyZ2V0X0RhdGEpJERldGVjdGVkU2VnbWVudHMgPC0gcm93U3VtcyhMT1FfTWF0LCBuYS5ybSA9IFRSVUUpDQpmRGF0YSh0YXJnZXRfRGF0YSkkRGV0ZWN0aW9uUmF0ZSA8LQ0KICBmRGF0YSh0YXJnZXRfRGF0YSkkRGV0ZWN0ZWRTZWdtZW50cyAvIG5yb3cocERhdGEodGFyZ2V0X0RhdGEpKQ0KYGBgDQoNCkdlbmUgb2YgaW50ZXJlc3QgZGV0ZWN0aW9uIHRhYmxlDQoNCmBgYHtyIGdlbmVfb2ZfaW50ZXJlc3RfdGFibGV9DQpnb2kgPC0gYygiUERDRDEiLCAiQ0QyNzQiLCAiSUZORyIsICJDRDhBIiwgIkNENjgiLCAiRVBDQU0iLA0KICAgICAgICAgIktSVDE4IiwgIk5QSFMxIiwgIk5QSFMyIiwgIkNBTEIxIiwgIkNMRE44IikNCmdvaV9kZiA8LSBkYXRhLmZyYW1lKA0KICBHZW5lID0gZ29pLA0KICBOdW1iZXIgPSBmRGF0YSh0YXJnZXRfRGF0YSlbZ29pLCAiRGV0ZWN0ZWRTZWdtZW50cyJdLA0KICBEZXRlY3Rpb25SYXRlID0gcGVyY2VudChmRGF0YSh0YXJnZXRfRGF0YSlbZ29pLCAiRGV0ZWN0aW9uUmF0ZSJdKSkNCmBgYA0KDQojIDQuOCBHZW5lIEZpbHRlcmluZw0KDQpXZSB3aWxsIGdyYXBoIHRoZSB0b3RhbCBudW1iZXIgb2YgZ2VuZXMgZGV0ZWN0ZWQgaW4gZGlmZmVyZW50DQpwZXJjZW50YWdlcyBvZiBzZWdtZW50cy4gQmFzZWQgb24gdGhlIHZpc3VhbGl6YXRpb24gYmVsb3csIHdlIGNhbiBiZXR0ZXINCnVuZGVyc3RhbmQgZ2xvYmFsIGdlbmUgZGV0ZWN0aW9uIGluIG91ciBzdHVkeSBhbmQgc2VsZWN0IGhvdyBtYW55IGxvdw0KZGV0ZWN0ZWQgZ2VuZXMgdG8gZmlsdGVyIG91dCBvZiB0aGUgZGF0YXNldC4gR2VuZSBmaWx0ZXJpbmcgaW5jcmVhc2VzDQpwZXJmb3JtYW5jZSBvZiBkb3duc3RyZWFtIHN0YXRpc3RpY2FsIHRlc3RzIGFuZCBpbXByb3ZlcyBpbnRlcnByZXRhdGlvbg0Kb2YgdHJ1ZSBiaW9sb2dpY2FsIHNpZ25hbC4NCg0KUGxvdCBkZXRlY3Rpb24gcmF0ZQ0KDQpgYGB7ciBwbG90X2RldF9yYXRlfQ0KcGxvdF9kZXRlY3QgPC0gZGF0YS5mcmFtZShGcmVxID0gYygxLCA1LCAxMCwgMjAsIDMwLCA1MCkpDQpwbG90X2RldGVjdCROdW1iZXIgPC0NCiAgdW5saXN0KGxhcHBseShjKDAuMDEsIDAuMDUsIDAuMSwgMC4yLCAwLjMsIDAuNSksDQogICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkge3N1bShmRGF0YSh0YXJnZXRfRGF0YSkkRGV0ZWN0aW9uUmF0ZSA+PSB4KX0pKQ0KcGxvdF9kZXRlY3QkUmF0ZSA8LSBwbG90X2RldGVjdCROdW1iZXIgLyBucm93KGZEYXRhKHRhcmdldF9EYXRhKSkNCnJvd25hbWVzKHBsb3RfZGV0ZWN0KSA8LSBwbG90X2RldGVjdCRGcmVxDQoNCmdncGxvdChwbG90X2RldGVjdCwgYWVzKHggPSBhcy5mYWN0b3IoRnJlcSksIHkgPSBSYXRlLCBmaWxsID0gUmF0ZSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGZvcm1hdEMoTnVtYmVyLCBmb3JtYXQgPSAiZCIsIGJpZy5tYXJrID0gIiwiKSksDQogICAgICAgICAgICB2anVzdCA9IDEuNiwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gNCkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAib3JhbmdlMiIsIG1pZCA9ICJsaWdodGJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICBoaWdoID0gImRvZGdlcmJsdWUzIiwgbWlkcG9pbnQgPSAwLjY1LA0KICAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAsMSksDQogICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKw0KICB0aGVtZV9idygpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCwgbGltaXRzID0gYygwLDEpLA0KICAgICAgICAgICAgICAgICAgICAgZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsIDApKSkgKw0KICBsYWJzKHggPSAiJSBvZiBTZWdtZW50cyIsDQogICAgICAgeSA9ICJHZW5lcyBEZXRlY3RlZCwgJSBvZiBQYW5lbCA+IExPUSIpDQpgYGANCg0KU3Vic2V0IHRvIHRhcmdldCBnZW5lcyBkZXRlY3RlZCBpbiBhdCBsZWFzdCAxMCUgb2YgdGhlIHNhbXBsZXMuIEFsc28NCm1hbnVhbGx5IGluY2x1ZGUgdGhlIG5lZ2F0aXZlIGNvbnRyb2wgcHJvYmUsIGZvciBkb3duc3RyZWFtIHVzZQ0KDQpgYGB7ciBzdWJzZXRfdG9fMTBwX2RldGVjdGVkX2dlbmVzfQ0KIyBkZWZhdWx0PTAuMQ0KbmVnYXRpdmVQcm9iZWZEYXRhIDwtIHN1YnNldChmRGF0YSh0YXJnZXRfRGF0YSksIENvZGVDbGFzcyA9PSAiTmVnYXRpdmUiKQ0KbmVnX3Byb2JlcyA8LSB1bmlxdWUobmVnYXRpdmVQcm9iZWZEYXRhJFRhcmdldE5hbWUpDQp0YXJnZXRfRGF0YSA8LSANCiAgdGFyZ2V0X0RhdGFbZkRhdGEodGFyZ2V0X0RhdGEpJERldGVjdGlvblJhdGUgPj0gMC4wNSB8DQogICAgICAgICAgICAgICAgICAgIGZEYXRhKHRhcmdldF9EYXRhKSRUYXJnZXROYW1lICVpbiUgbmVnX3Byb2JlcywgXQ0KDQojIHJldGFpbiBvbmx5IGRldGVjdGVkIGdlbmVzIG9mIGludGVyZXN0DQpnb2kgPC0gZ29pW2dvaSAlaW4lIHJvd25hbWVzKHRhcmdldF9EYXRhKV0NCg0KZGltKHRhcmdldF9EYXRhKQ0KYGBgDQoNCiMgNSBOb3JtYWxpemF0aW9uDQoNCldlIHdpbGwgbm93IG5vcm1hbGl6ZSB0aGUgR2VvTXggZGF0YSBmb3IgZG93bnN0cmVhbSB2aXN1YWxpemF0aW9ucyBhbmQNCmRpZmZlcmVudGlhbCBleHByZXNzaW9uLiBUaGUgdHdvIGNvbW1vbiBtZXRob2RzIGZvciBub3JtYWxpemF0aW9uIG9mDQpEU1AtTkdTIFJOQSBkYXRhIGFyZSBpKSBxdWFydGlsZSAzIChRMykgb3IgaWkpIGJhY2tncm91bmQgbm9ybWFsaXphdGlvbi4NCg0KQm90aCBvZiB0aGVzZSBub3JtYWxpemF0aW9uIG1ldGhvZHMgZXN0aW1hdGUgYSBub3JtYWxpemF0aW9uIGZhY3RvciBwZXINCnNlZ21lbnQgdG8gYnJpbmcgdGhlIHNlZ21lbnQgZGF0YSBkaXN0cmlidXRpb25zIHRvZ2V0aGVyLiBNb3JlIGFkdmFuY2VkDQptZXRob2RzIGZvciBub3JtYWxpemF0aW9uIGFuZCBtb2RlbGluZyBhcmUgdW5kZXIgYWN0aXZlIGRldmVsb3BtZW50Lg0KSG93ZXZlciwgZm9yIG1vc3Qgc3R1ZGllcywgdGhlc2UgbWV0aG9kcyBhcmUgc3VmZmljaWVudCBmb3INCnVuZGVyc3RhbmRpbmcgZGlmZmVyZW5jZXMgYmV0d2VlbiBiaW9sb2dpY2FsIGNsYXNzZXMgb2Ygc2VnbWVudHMgYW5kDQpzYW1wbGVzLg0KDQpRMyBub3JtYWxpemF0aW9uIGlzIHR5cGljYWxseSB0aGUgcHJlZmVycmVkIG5vcm1hbGl6YXRpb24gc3RyYXRlZ3kgZm9yDQptb3N0IERTUC1OR1MgUk5BIHN0dWRpZXMuIEdpdmVuIHRoZSBsb3cgbmVnYXRpdmUgcHJvYmUgY291bnRzIGluIHRoaXMNCnBhcnRpY3VsYXIgZGF0YXNldCBhcyBzaG93biBkdXJpbmcgU2VnbWVudCBRQywgd2Ugd291bGQgZnVydGhlciBhdm9pZA0KYmFja2dyb3VuZCBub3JtYWxpemF0aW9uIGFzIGl0IG1heSBiZSBsZXNzIHN0YWJsZS4NCg0KQmVmb3JlIG5vcm1hbGl6YXRpb24sIHdlIHdpbGwgZXhwbG9yZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHVwcGVyDQpxdWFydGlsZSAoUTMpIG9mIHRoZSBjb3VudHMgaW4gZWFjaCBzZWdtZW50IHdpdGggdGhlIGdlb21ldHJpYyBtZWFuIG9mDQp0aGUgbmVnYXRpdmUgY29udHJvbCBwcm9iZXMgaW4gdGhlIGRhdGEuIElkZWFsbHksIHRoZXJlIHNob3VsZCBiZSBhDQpzZXBhcmF0aW9uIGJldHdlZW4gdGhlc2UgdHdvIHZhbHVlcyB0byBlbnN1cmUgd2UgaGF2ZSBzdGFibGUgbWVhc3VyZSBvZg0KUTMgc2lnbmFsLiBJZiB5b3UgZG8gbm90IHNlZSBzdWZmaWNpZW50IHNlcGFyYXRpb24gYmV0d2VlbiB0aGVzZSB2YWx1ZXMsDQp5b3UgbWF5IGNvbnNpZGVyIG1vcmUgYWdncmVzc2l2ZSBmaWx0ZXJpbmcgb2YgbG93IHNpZ25hbCBzZWdtZW50cy9nZW5lcy4NCg0KR3JhcGggUTMgdmFsdWUgdnMgbmVnR2VvTWVhbiBvZiBOZWdhdGl2ZXMNCg0KYGBge3IgbHBvdF9xM19uZWdHZW9NZWFufQ0KYW5uX29mX2ludGVyZXN0IDwtICJBTk4yIg0KU3RhdF9kYXRhIDwtIA0KICBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGNvbG5hbWVzKGV4cHJzKHRhcmdldF9EYXRhKSksDQogICAgICAgICAgICAgU2VnbWVudCA9IGNvbG5hbWVzKGV4cHJzKHRhcmdldF9EYXRhKSksDQogICAgICAgICAgICAgQW5ub3RhdGlvbiA9IHBEYXRhKHRhcmdldF9EYXRhKVssIGFubl9vZl9pbnRlcmVzdF0sDQogICAgICAgICAgICAgUTMgPSB1bmxpc3QoYXBwbHkoZXhwcnModGFyZ2V0X0RhdGEpLCAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1YW50aWxlLCAwLjc1LCBuYS5ybSA9IFRSVUUpKSwNCiAgICAgICAgICAgICBOZWdQcm9iZSA9IGV4cHJzKHRhcmdldF9EYXRhKVtuZWdfcHJvYmVzLCBdKQ0KU3RhdF9kYXRhX20gPC0gbWVsdChTdGF0X2RhdGEsIG1lYXN1cmUudmFycyA9IGMoIlEzIiwgIk5lZ1Byb2JlIiksDQogICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLm5hbWUgPSAiU3RhdGlzdGljIiwgdmFsdWUubmFtZSA9ICJWYWx1ZSIpDQoNCnBsdDEgPC0gZ2dwbG90KFN0YXRfZGF0YV9tLA0KICAgICAgICAgICAgICAgYWVzKHggPSBWYWx1ZSwgZmlsbCA9IFN0YXRpc3RpYykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDQwKSArIHRoZW1lX2J3KCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMiIpICsNCiAgZmFjZXRfd3JhcCh+QW5ub3RhdGlvbiwgbnJvdyA9IDEpICsgDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAzLCB0eXBlID0gInF1YWwiKSArDQogIGxhYnMoeCA9ICJDb3VudHMiLCB5ID0gIlNlZ21lbnRzLCAjIikNCg0KcGx0MiA8LSBnZ3Bsb3QoU3RhdF9kYXRhLA0KICAgICAgICAgICAgICAgYWVzKHggPSBOZWdQcm9iZSwgeSA9IFEzLCBjb2xvciA9IEFubm90YXRpb24pKSArDQogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSwgbHR5ID0gImRhc2hlZCIsIGNvbG9yID0gImRhcmtncmF5IikgKw0KICBnZW9tX3BvaW50KCkgKyBndWlkZXMoY29sb3IgPSAibm9uZSIpICsgdGhlbWVfYncoKSArDQogIHNjYWxlX3hfY29udGludW91cyh0cmFucyA9ICJsb2cyIikgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArDQogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsNCiAgbGFicyh4ID0gIk5lZ2F0aXZlIFByb2JlIEdlb01lYW4sIENvdW50cyIsIHkgPSAiUTMgVmFsdWUsIENvdW50cyIpDQoNCnBsdDMgPC0gZ2dwbG90KFN0YXRfZGF0YSwNCiAgICAgICAgICAgICAgIGFlcyh4ID0gTmVnUHJvYmUsIHkgPSBRMyAvIE5lZ1Byb2JlLCBjb2xvciA9IEFubm90YXRpb24pKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEsIGx0eSA9ICJkYXNoZWQiLCBjb2xvciA9ICJkYXJrZ3JheSIpICsNCiAgZ2VvbV9wb2ludCgpICsgdGhlbWVfYncoKSArDQogIHNjYWxlX3hfY29udGludW91cyh0cmFucyA9ICJsb2cyIikgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArDQogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsNCiAgbGFicyh4ID0gIk5lZ2F0aXZlIFByb2JlIEdlb01lYW4sIENvdW50cyIsIHkgPSAiUTMvTmVnUHJvYmUgVmFsdWUsIENvdW50cyIpDQoNCmJ0bV9yb3cgPC0gcGxvdF9ncmlkKHBsdDIsIHBsdDMsIG5yb3cgPSAxLCBsYWJlbHMgPSBjKCJCIiwgIiIpLA0KICAgICAgICAgICAgICAgICAgICAgcmVsX3dpZHRocyA9IGMoMC40MywwLjU3KSkNCnBsb3RfZ3JpZChwbHQxLCBidG1fcm93LCBuY29sID0gMSwgbGFiZWxzID0gYygiQSIsICIiKSkNCg0KYGBgDQoNClEzIG5vcm0gKDc1dGggcGVyY2VudGlsZSkgZm9yIFdUQS9DVEEgd2l0aCBvciB3aXRob3V0IGN1c3RvbSBzcGlrZS1pbnMNCg0KYGBge3IgcTNfbm9ybX0NCnRhcmdldF9EYXRhIDwtIG5vcm1hbGl6ZSh0YXJnZXRfRGF0YSAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fbWV0aG9kID0gInF1YW50IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lyZWRRdWFudGlsZSA9IC43NSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9FbHQgPSAicV9ub3JtIikNCiMsIGRhdGFfdHlwZSA9ICJSTkEiIGRlcHJpY2F0ZWQgYWZ0ZXIgNC4xDQoNCmBgYA0KDQpRdWFudGlsZSBOb3JtYWxpemF0aW9uDQoNCmBgYHtyIHF1YW50aWxlfQ0KcXVhbnRpbGUgPC0gbm9ybWFsaXplLnF1YW50aWxlcyh0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJleHBycyJdXSkNCnJvd25hbWVzKHF1YW50aWxlKSA8LSByb3duYW1lcyh0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJleHBycyJdXSkNCmNvbG5hbWVzKHF1YW50aWxlKSA8LSBjb2xuYW1lcyh0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJleHBycyJdXSkNCg0KYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gInF1YW50aWxlX25vcm0iKSA8LSAgcXVhbnRpbGUNCmBgYA0KDQpCYWNrZ3JvdW5kIG5vcm1hbGl6YXRpb24gZm9yIFdUQS9DVEEgd2l0aG91dCBjdXN0b20gc3Bpa2UtaW4NCg0KYGBge3IgYmdfbm9ybX0NCnRhcmdldF9EYXRhIDwtIG5vcm1hbGl6ZSh0YXJnZXRfRGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9tZXRob2QgPSAibmVnIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZyb21FbHQgPSAiZXhwcnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b0VsdCA9ICJuZWdfbm9ybSIpDQoNCiMgLCBkYXRhX3R5cGUgPSAiUk5BIiBkZXByaWNhdGVkIGFmdGVyIDQuMQ0KYGBgDQoNCiMgNS4xIFZpc3VhbGl6ZSB0aGUgZmlyc3QgMTAgc2VnbWVudHMgd2l0aCBlYWNoIG5vcm1hbGl6YXRpb24gbWV0aG9kIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNClVzZSB0aGUgdGFiLW1lbnUgdG8gbmF2aWdhdGUhDQoNCmBgYHtyIHZpc3VsYWl6ZV9ub3Jtc30NCg0KI0ZpeCB6ZXJvIHZhbHVlcywgd2hpY2ggZ28gdG8gLWluZiBpbiBsb2cgdHJhbnNmb3JtIGluIHN0YW5kYXJkIGJveHBsb3QNCiMgdGVtcCA8LWFzLm1hdHJpeChleHBycygodGFyZ2V0X0RhdGEpWywxOjEwXSkpDQojIGxvbmcgPC0gbWVsdCh0ZW1wKQ0KIyBjb2xuYW1lcyhsb25nKSA8LSBjKCJnZW5lIiwic2VnbWVudCIsImNvdW50IikNCiMgZ2dwbG90KGxvbmcsIGFlcyh4PXNlZ21lbnQseT1jb3VudCkpICsNCiMgICAgIGdlb21fYm94cGxvdChmaWxsPSIjOUVEQUU1IikgKw0KIyAgICAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPXNjYWxlczo6cHNldWRvX2xvZ190cmFucyhiYXNlID0gMTApKSArDQojICAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz1jKDE6MTApKSArDQojICAgICBsYWJzKHRpdGxlPSJSYXcgY291bnRzIiwgeD0ic2VnbWVudCIsIHkgPSAiQ291bnRzLCBSYXciKQ0KIyANCiMgDQojIHRlbXAgPC1hcy5tYXRyaXgoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVssMToxMF0sIGVsdCA9ICJxX25vcm0iKSkNCiMgbG9uZyA8LSBtZWx0KHRlbXApDQojIGNvbG5hbWVzKGxvbmcpIDwtIGMoImdlbmUiLCJzZWdtZW50IiwiY291bnQiKQ0KIyBnZ3Bsb3QobG9uZywgYWVzKHg9c2VnbWVudCx5PWNvdW50KSkgKw0KIyAgICAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiIzJDQTAyQyIpICsNCiMgICAgIHNjYWxlX3lfY29udGludW91cyh0cmFucz1zY2FsZXM6OnBzZXVkb19sb2dfdHJhbnMoYmFzZSA9IDEwKSkgKw0KIyAgICAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YygxOjEwKSkgKw0KIyAgICAgbGFicyh0aXRsZT0iUTMgTm9ybSBDb3VudHMiLCB4PSJzZWdtZW50IiwgeSA9ICJDb3VudHMsIFEzIE5vcm1hbGl6ZWQiKQ0KIyANCiMgDQojIHRlbXAgPC1hcy5tYXRyaXgoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVssMToxMF0sIGVsdCA9ICJuZWdfbm9ybSIpKQ0KIyBsb25nIDwtIG1lbHQodGVtcCkNCiMgY29sbmFtZXMobG9uZykgPC0gYygiZ2VuZSIsInNlZ21lbnQiLCJjb3VudCIpDQojIGdncGxvdChsb25nLCBhZXMoeD1zZWdtZW50LHk9Y291bnQpKSArDQojICAgICBnZW9tX2JveHBsb3QoZmlsbCA9ICIjRkY3RjBFIikgKw0KIyAgICAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPXNjYWxlczo6cHNldWRvX2xvZ190cmFucyhiYXNlID0gMTApKSArDQojICAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz1jKDE6MTApKSArDQojICAgICBsYWJzKHRpdGxlPSJOZWcgTm9ybSBDb3VudHMiLCB4PSJzZWdtZW50IiwgeSA9ICJDb3VudHMsIE5lZy4gTm9ybWFsaXplZCIpDQpgYGANCg0KIyMgcmF3IGNvdW50cw0KDQpgYGB7cn0NCmJveHBsb3QoZXhwcnModGFyZ2V0X0RhdGEpWywxOjhdLA0KICAgICAgICBjb2wgPSAiIzlFREFFNSIsIG1haW4gPSAiUmF3IENvdW50cyIsDQogICAgICAgIGxvZyA9ICJ5IiwgbmFtZXMgPSAxOjgsIHhsYWIgPSAiU2VnbWVudCIsDQogICAgICAgIHlsYWIgPSAiQ291bnRzLCBSYXciKQ0KYGBgDQoNCiMjIFEzIG5vcm1hbGl6ZWQgey5hY3RpdmV9DQoNCmBgYHtyfQ0KYm94cGxvdChhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhWywxOjhdLCBlbHQgPSAicV9ub3JtIiksDQogICAgICAgIGNvbCA9ICIjMkNBMDJDIiwgbWFpbiA9ICJRMyBOb3JtIENvdW50cyIsDQogICAgICAgIGxvZyA9ICJ5IiwgbmFtZXMgPSAxOjgsIHhsYWIgPSAiU2VnbWVudCIsDQogICAgICAgIHlsYWIgPSAiQ291bnRzLCBRMyBOb3JtYWxpemVkIikNCmBgYA0KDQojIyBRdWFudGlsZSBub3JtYWxpemVkICh0ZXN0aW5nIHBoYXNlKQ0KDQpgYGB7cn0NCmJveHBsb3QoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVssMTo4XSwgZWx0ID0gInF1YW50aWxlX25vcm0iKSwNCiAgICAgICAgY29sID0gInBpbmsiLCBtYWluID0gIlF1YW50aWxlIE5vcm0gQ291bnRzIiwNCiAgICAgICAgbG9nID0gInkiLCBuYW1lcyA9IDE6OCwgeGxhYiA9ICJTZWdtZW50IiwNCiAgICAgICAgeWxhYiA9ICJDb3VudHMsIFF1YW50aWxlIE5vcm1hbGl6ZWQiKQ0KYGBgDQoNCiMjIE5lZ2F0aXZlIHByb2JlIG5vcm1hbGl6YXRpb24NCg0KYGBge3J9DQpib3hwbG90KGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGFbLDE6OF0sIGVsdCA9ICJuZWdfbm9ybSIpLA0KICAgICAgICBjb2wgPSAiI0ZGN0YwRSIsIG1haW4gPSAiTmVnIE5vcm0gQ291bnRzIiwNCiAgICAgICAgbG9nID0gInkiLCBuYW1lcyA9IDE6OCwgeGxhYiA9ICJTZWdtZW50IiwNCiAgICAgICAgeWxhYiA9ICJDb3VudHMsIE5lZy4gTm9ybWFsaXplZCIpDQoNCmBgYA0KDQojIDYgVW5zdXBlcnZpc2VkIEFuYWx5c2lzDQoNCiMgNi4xIFVNQVAgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KVXNlIHRoZSB0YWItbWVudSB0byBuYXZpZ2F0ZSENCg0KIyMgMQ0KDQpgYGB7ciB1bWFwLCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD04fQ0KY3VzdG9tX3VtYXAgPC0gdW1hcDo6dW1hcC5kZWZhdWx0cw0KY3VzdG9tX3VtYXAkcmFuZG9tX3N0YXRlIDwtIDQyDQojIHJ1biBVTUFQDQoNCnVtYXBfb3V0IDwtDQogIHVtYXAodChsb2cyKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEgLCBlbHQgPSAicV9ub3JtIikpKSwNCiAgICAgICBjb25maWcgPSBjdXN0b21fdW1hcCkNCnBEYXRhKHRhcmdldF9EYXRhKVssIGMoIlVNQVAxIiwgIlVNQVAyIildIDwtIHVtYXBfb3V0JGxheW91dFssIGMoMSwyKV0NCmdncGxvdChwRGF0YSh0YXJnZXRfRGF0YSksDQogICAgICAgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyLCBjb2xvciA9IHNsaWRlX25hbWUsIHNoYXBlID0gQU5OMSkpICsNCg0KICBnZW9tX3BvaW50KHNpemUgPSAzKSArDQogICNnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsPXJvdy5uYW1lcyhwRGF0YSh0YXJnZXRfRGF0YSkpKSwgc2l6ZT0yLG1heC5vdmVybGFwcyA9IDEwMCkrDQogIHRoZW1lX2J3KCkNCg0KYmF0Y2hfY29ycmVjdGlvbl9uZWVkZWQgPSBGQUxTRQ0KYGBgDQoNCiMjIDINCg0KYGBge3IgdW1hcDIsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTh9DQpnZ3Bsb3QocERhdGEodGFyZ2V0X0RhdGEpLA0KICAgICAgIGFlcyh4ID0gVU1BUDEsIHkgPSBVTUFQMiwgY29sb3IgPSBBTk4zLCBzaGFwZSA9IEFOTjEpKSArDQoNCiAgZ2VvbV9wb2ludChzaXplID0gMykgKw0KICAjZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbD1yb3cubmFtZXMocERhdGEodGFyZ2V0X0RhdGEpKSksIHNpemU9MixtYXgub3ZlcmxhcHMgPSAxMDApKw0KICB0aGVtZV9idygpDQpgYGANCg0KIyA2LjIgQmF0Y2ggY29ycmVjdGlvbiB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpJZiB0aGUgZGF0YSBpcyBvYnNlcnZlZCB0byBoYXZlIGEgYmF0Y2ggZWZmZWN0IGl0IGNhbiBiZSBjb3JyZWN0ZWQgd2l0aCB0aGUgbWV0aG9kczogUlVWNCwgTElNTUEsIG9yIENvbWJhdC1TZXEuIEVhY2ggbWV0aG9kIGlzIGRvbmUgYW5kIHRoZSBiZXN0IG9uZSBpcyBwaWNrZWQgYW5kIHVzZWQuDQoNClRoZSBzdGFuZFIgcGFja2FnZSBpcyBzaG9ydCBmb3IgU3BhdGlhbCB0cmFuc2NyaXB0b21pY3MgYW5hbHl6ZXMgYW5kIGRlY29kaW5nIGluIFIsIGl0IGFpbXMgYXQgcHJvdmlkaW5nIGdvb2QgcHJhY3RpY2UgcGlwZWxpbmUgYW5kIHVzZWZ1bCBmdW5jdGlvbnMgZm9yIHVzZXJzIHRvIGFuYWx5emUgTmFub3N0cmluZ+KAmXMgR2VvTXggRFNQIGRhdGEuIEluIHRoZSBOYW5vc3RyaW5n4oCZcyBHZW9NWCBEU1AgcHJvdG9jb2wsIGR1ZSB0byB0aGUgZmFjdCB0aGF0IG9uZSBzbGlkZSBpcyBvbmx5IGJpZyBlbm91Z2ggZm9yIGEgaGFuZGZ1bCBvZiB0aXNzdWUgc2VnbWVudHMgKFJPSXMpLCBpdCBpcyBjb21tb24gdGhhdCB3ZSBzZWUgdGhlIERTUCBkYXRhIGJlaW5nIGNvbmZvdW5kZWQgYnkgdGhlIGJhdGNoIGVmZmVjdCBpbnRyb2R1Y2VkIGJ5IGRpZmZlcmVudCBzbGlkZXMuIEluIG9yZGVyIHRvIGVzdGFibGlzaCBmYWlyIGNvbXBhcmlzb24gYmV0d2VlbiBST0lzIGxhdGVyIG9uLCBpdCBpcyBuZWNlc3NhcnkgdG8gcmVtb3ZlIHRoaXMgYmF0Y2ggZWZmZWN0IGZyb20gdGhlIGRhdGEuIChodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9zdGFuZFIvaW5zdC9kb2Mvc3RhbmRSX2ludHJvZHVjdGlvbi5odG1sKQ0KDQpGb3IgUlVWNCBjb3JyZWN0aW9uLCB0aGUgZnVuY3Rpb24gaXMgcmVxdWlyaW5nIDMgcGFyYW1ldGVycyBvdGhlciB0aGFuIHRoZSBpbnB1dCBvYmplY3QsIGluY2x1ZGluZyBmYWN0b3JzOiB0aGUgZmFjdG9yIG9mIGludGVyZXN0LCBpLmUuIHRoZSBiaW9sb2dpY2FsIHZhcmlhdGlvbiB3ZSBwbGFuIHRvIGtlZXA7IE5DR3M6IHRoZSBsaXN0IG9mIG5lZ2F0aXZlIGNvbnRyb2wgZ2VuZXMgZGV0ZWN0ZWQgdXNpbmcgdGhlIGZ1bmN0aW9uIGZpbmROQ0dzOyBhbmQgazogaXMgdGhlIG51bWJlciBvZiB1bndhbnRlZCBmYWN0b3JzIHRvIHVzZSwgaW4gdGhlIFJVViBkb2N1bWVudGF0aW9uLCBpdCBpcyBzdWdnZXN0IHRoYXQgd2Ugc2hvdWxkIHVzZSB0aGUgc21hbGxlc3QgayBvbmNlIHdlIGRvbuKAmXQgb2JzZXJ2ZSB0ZWNobmljYWwgdmFyaWF0aW9uIGluIHRoZSBkYXRhLg0KDQpBbm90aGVyIG9wdGlvbiBpcyBzZXQgdGhlIHBhcmFtZXRlciBtZXRob2QgdG8g4oCcTGltbWHigJ0sIHdoaWNoIHVzZXMgdGhlIHJlbW92ZSBiYXRjaCBjb3JyZWN0aW9uIG1ldGhvZCBmcm9tIGxpbW1hLiBJbiB0aGlzIG1vZGUsIHRoZSBmdW5jdGlvbiBpcyByZXF1aXJpbmcgMiBwYXJhbWV0ZXJzLCBpbmNsdWRpbmcgYmF0Y2g6IGEgdmVjdG9yIHRoYXQgaW5kaWNhdGluZyBiYXRjaGVzIGZvciBhbGwgc2FtcGxlczsgYW5kIGRlc2lnbjogYSBkZXNpZ24gbWF0cml4IHdoaWNoIGlzIGdlbmVyYXRlZCBieSBtb2RlbC5tYXRyaXgsIGluIHRoZSBkZXNpZ24gbWF0cml4LCBhbGwgYmlvbG9naWNhbGx5LXJlbGV2YW50IGZhY3RvcnMgc2hvdWxkIGJlIGluY2x1ZGVkLg0KDQpDb21CYXQtc2VxIGlzIGEgYmF0Y2ggZWZmZWN0IGFkanVzdG1lbnQgdG9vbCBmb3IgYnVsayBSTkEtc2VxIGNvdW50IGRhdGEuIEl0IGlzIGFuIGltcHJvdmVkIG1vZGVsIGJhc2VkIG9uIHRoZSBwb3B1bGFyIENvbUJhdCwgdG8gYWRkcmVzcyBpdHMgbGltaXRhdGlvbnMgdGhyb3VnaCBub3ZlbCBtZXRob2RzIGRlc2lnbmVkIHNwZWNpZmljYWxseSBmb3IgUk5BLVNlcSBzdHVkaWVzLiBDb21CYXQtc2VxIHRha2VzIHVudHJhbnNmb3JtZWQsIHJhdyBjb3VudCBtYXRyaXggYXMgaW5wdXQuIFNhbWUgYXMgQ29tQmF0LCBpdCByZXF1aXJlcyBhIGtub3duIGJhdGNoIHZhcmlhYmxlLiAoaHR0cHM6Ly9naXRodWIuY29tL3poYW5neXVxaW5nL0NvbUJhdC1zZXEpDQoNCiMjIFNldCB1cCBzdGFuZFIgb2JqZWN0DQoNCmBgYHtyIGJhdGNoIGNvcnJlY3Rpb24sIGV2YWw9YmF0Y2hfY29ycmVjdGlvbl9uZWVkZWR9DQpjb3VudF9nZW9teCA8LSBhcy5kYXRhLmZyYW1lKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImV4cHJzIl1dKQ0KDQpzYW1wbGVfZ2VvbXggPC0gdGFyZ2V0X0RhdGFAcGhlbm9EYXRhQGRhdGENCnNhbXBsZV9nZW9teCRyb2kgPC0gdGFyZ2V0X0RhdGFAcHJvdG9jb2xEYXRhQGRhdGEkcm9pDQpzYW1wbGVfZ2VvbXggPC0gYXMuZGF0YS5mcmFtZShzYW1wbGVfZ2VvbXgpDQpzYW1wbGVfZ2VvbXgkU2FtcGxlX0lEIDwtIHJvd25hbWVzKHNhbXBsZV9nZW9teCkNCnNhbXBsZV9nZW9teCRTZWdtZW50RGlzcGxheU5hbWUgPC0gcGFzdGUoc2FtcGxlX2dlb214JGBzY2FuIG5hbWVgLCBzYW1wbGVfZ2VvbXgkcm9pLCBzYW1wbGVfZ2VvbXgkc2VnbWVudCwgc2VwID0gIiB8ICIpDQpzYW1wbGVfZ2VvbXgkUk9JQ29vcmRpbmF0ZVggPC0gMQ0Kc2FtcGxlX2dlb214JFJPSUNvb3JkaW5hdGVZIDwtIDENCg0KDQpmZWF0dXJlX2dlb214IDwtIGZEYXRhKERhdGEpDQpmZWF0dXJlX2dlb214IDwtIGZlYXR1cmVfZ2VvbXhbYygiUlRTX0lEIiwgIlRhcmdldE5hbWUiLCAiUHJvYmVJRCIsICJOZWdhdGl2ZSIpXQ0KZmVhdHVyZV9nZW9teCA8LSBhcy5kYXRhLmZyYW1lKGZlYXR1cmVfZ2VvbXgpDQpyb3duYW1lcyhmZWF0dXJlX2dlb214KSA8LSBOVUxMDQoNCm1hdGNoaW5nIDwtIHNhbXBsZV9nZW9teCRTZWdtZW50RGlzcGxheU5hbWVbc2FtcGxlX2dlb214JFNhbXBsZV9JRCAlaW4lIGNvbG5hbWVzKGNvdW50X2dlb214KV0NCmNvbG5hbWVzKGNvdW50X2dlb214KSA8LSBtYXRjaGluZw0KY291bnRfZ2VvbXgkVGFyZ2V0TmFtZSA8LSByb3duYW1lcyhjb3VudF9nZW9teCkNCnJvd25hbWVzKGNvdW50X2dlb214KSA8LSBOVUxMDQoNCnNwZSA8LSByZWFkR2VvTXgoY291bnRfZ2VvbXgsIHNhbXBsZV9nZW9teCwgZmVhdHVyZUFubm9GaWxlID0gZmVhdHVyZV9nZW9teCwgaGFzTmVnUHJvYmUgPSBUUlVFKQ0KDQpjb2xEYXRhKHNwZSkkcmVnaW9ucyA8LSBwYXN0ZTAoY29sRGF0YShzcGUpJEFOTjIsIl8iLGNvbERhdGEoc3BlKSRBTk4xKSB8PiANCiAgKFwoLikgZ3N1YigiX0dlb21ldHJpYyBTZWdtZW50IiwiIiwuKSkoKSB8Pg0KICBwYXN0ZTAoIl8iLGNvbERhdGEoc3BlKSRwYXRob2xvZ3kpIHw+DQogIChcKC4pIGdzdWIoIl9OQSIsIl9ucyIsLikpKCkNCg0KY29sRGF0YShzcGUpJHJlZ2lvbnMgPC0gcGFzdGUwKGNvbERhdGEoc3BlKSRBTk4yLCJfIixjb2xEYXRhKHNwZSkkQU5OMSkgfD4gDQogIChcKC4pIGdzdWIoIl9HZW9tZXRyaWMgU2VnbWVudCIsIiIsLikpKCkgfD4NCiAgcGFzdGUwKCJfIixjb2xEYXRhKHNwZSkkcGF0aG9sb2d5KSB8Pg0KICAoXCguKSBnc3ViKCJfTkEiLCJfbnMiLC4pKSgpDQpjb2xEYXRhKHNwZSkkYmlvbG9neSA8LSBwYXN0ZTAoY29sRGF0YShzcGUpJHJlZ2lvbnMpDQpgYGANCg0KIyMgU2VlIG9wdGltYWwgayB2YWx1ZSBmb3IgUlVWNA0KDQpgYGB7ciwgZXZhbD1iYXRjaF9jb3JyZWN0aW9uX25lZWRlZH0NCnNwZSA8LSBmaW5kTkNHcyhzcGUsIGJhdGNoX25hbWUgPSAic2xpZGVfbmFtZSIsIHRvcF9uID0gNTAwKQ0KZmluZEJlc3RLKHNwZSwgbWF4SyA9IDEwLCBmYWN0b3Jfb2ZfaW50ID0gImJpb2xvZ3kiLCBOQ0dzID0gbWV0YWRhdGEoc3BlKSROQ0dzLCBmYWN0b3JfYmF0Y2ggPSAic2xpZGVfbmFtZSIpDQpgYGANCg0KIyMgUlVWNA0KDQpgYGB7ciBydXY0IGJhdGNoLCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD0xMCwgZXZhbD1iYXRjaF9jb3JyZWN0aW9uX25lZWRlZH0NCnJ1djQgPC0gZ2VvbXhCYXRjaENvcnJlY3Rpb24oc3BlLCBmYWN0b3JzID0gImJpb2xvZ3kiLCANCiAgICAgICAgICAgICAgICAgICBOQ0dzID0gbWV0YWRhdGEoc3BlKSROQ0dzLCBrID0gMSkNCg0KcGxvdFBhaXJQQ0EocnV2NCwgYXNzYXkgPSAyLCBjb2xvciA9IHNsaWRlX25hbWUsIHNoYXBlID0gcmVnaW9ucywgdGl0bGUgPSAiUlVWNCByZW1vdmVCYXRjaCIpDQoNCnBsb3RSTEV4cHIocnV2NCwgYXNzYXkgPSAyLCBjb2xvciA9IGBzbGlkZV9uYW1lYCkgKyBnZ3RpdGxlKCJSVVY0IHJlbW92ZUJhdGNoIikNCmBgYA0KDQojIyBMaW1tYQ0KDQpgYGB7ciBsaW1tYSBiYXRjaCwgIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTEwLCBldmFsPWJhdGNoX2NvcnJlY3Rpb25fbmVlZGVkfQ0KbGltbWEgPC0gZ2VvbXhCYXRjaENvcnJlY3Rpb24oc3BlLA0KICAgICAgICAgICAgICAgICAgICAgICBiYXRjaCA9IGNvbERhdGEoc3BlKSRgc2xpZGVfbmFtZWAsIG1ldGhvZCA9ICJMaW1tYSIsDQogICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IG1vZGVsLm1hdHJpeCh+IDAgKyBBTk4xICsgcmVnaW9ucywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gY29sRGF0YShzcGUpKSkNCg0KcGxvdFBhaXJQQ0EobGltbWEsIGFzc2F5ID0gMiwgY29sb3IgPSBzbGlkZV9uYW1lLCBzaGFwZSA9IHJlZ2lvbnMsIHRpdGxlID0gIkxpbW1hIHJlbW92ZUJhdGNoIikNCg0KcGxvdFJMRXhwcihsaW1tYSwgYXNzYXkgPSAyLCBjb2xvciA9IHNsaWRlX25hbWUpICsgZ2d0aXRsZSgiTGltbWEgcmVtb3ZlQmF0Y2giKQ0KYGBgDQoNCiMjIENvbWJhdFNlcQ0KDQpgYGB7ciwgZXZhbD1iYXRjaF9jb3JyZWN0aW9uX25lZWRlZH0NCmFkanVzdGVkIDwtIENvbUJhdF9zZXEodGFyZ2V0X0RhdGFAYXNzYXlEYXRhW1siZXhwcnMiXV0sIGJhdGNoPXRhcmdldF9EYXRhQHBoZW5vRGF0YUBkYXRhW1sic2xpZGVfbmFtZSJdXSwgZ3JvdXA9dGFyZ2V0X0RhdGFAcGhlbm9EYXRhQGRhdGFbWyJBTk4yIl1dKQ0KYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gImNvbWJhdCIpIDwtICBhZGp1c3RlZA0KDQp1bWFwX291dDIgPC0NCiAgdW1hcCh0KGxvZzIoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSAsIGVsdCA9ICJjb21iYXQiKSkpLA0KICAgICAgIGNvbmZpZyA9IGN1c3RvbV91bWFwKQ0KcERhdGEodGFyZ2V0X0RhdGEpWywgYygiVU1BUDEiLCAiVU1BUDIiKV0gPC0gdW1hcF9vdXQyJGxheW91dFssIGMoMSwyKV0NCmdncGxvdChwRGF0YSh0YXJnZXRfRGF0YSksDQogICAgICAgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyLCBjb2xvciA9IHNsaWRlX25hbWUsIHNoYXBlID0gQU5OMSkpICsNCg0KICBnZW9tX3BvaW50KHNpemUgPSAzKSArDQogICNnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsPXJvdy5uYW1lcyhwRGF0YSh0YXJnZXRfRGF0YSkpKSwgc2l6ZT0yLG1heC5vdmVybGFwcyA9IDEwMCkrDQogIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJDb21iYXRTZXEgcmVtb3ZlQmF0Y2giKQ0KYGBgDQoNCiMjIENvbXBhcmUgbGltbWEgdnMgUlVWNA0KDQpgYGB7ciBjb21wYXJlIGJhdGNoLCBldmFsPWJhdGNoX2NvcnJlY3Rpb25fbmVlZGVkfQ0Kc3BlX2xpc3QgPC0gbGlzdChzcGUsIHJ1djQsIGxpbW1hKQ0KDQpwbG90Q2x1c3RlckV2YWxTdGF0cyhzcGVfbGlzdCA9IHNwZV9saXN0LA0KICAgICAgICAgICAgICAgICAgICAgYmlvX2ZlYXR1cmVfbmFtZSA9ICJyZWdpb25zIiwNCiAgICAgICAgICAgICAgICAgICAgIGJhdGNoX2ZlYXR1cmVfbmFtZSA9ICJzbGlkZV9uYW1lIiwNCiAgICAgICAgICAgICAgICAgICAgIGRhdGFfbmFtZXMgPSBjKCJSYXciLCJSVVY0IiwiTGltbWEiKSkNCmBgYA0KDQojIyBBZGQgYmF0Y2ggY29ycmVjdGlvbiByZXN1bHQgdG8gdGFyZ2V0X0RhdGENCg0KYGBge3IgYWRkIGJhdGNoIGNvcnJlY3Rpb24sIGV2YWw9YmF0Y2hfY29ycmVjdGlvbl9uZWVkZWR9DQpuZWdfcHJvYmVzX3NhdmUgPC0gdChhcy5tYXRyaXgodGFyZ2V0X0RhdGFAYXNzYXlEYXRhJHFfbm9ybVsiTmVnUHJvYmUtV1RYIixdKSkNCnJvd25hbWVzKG5lZ19wcm9iZXNfc2F2ZSkgPC0gIk5lZ1Byb2JlLVdUWCINCg0KIyBEZXBlbmRpbmcgb24gY29ycmVjdCBtZXRob2QsIGNoYW5nZSB0aGUgd29yZCAibGltbWEiIHRvICJydXY0IiBvciB2aWNlIHZlcnNhLg0KbGltbWEgPC1saW1tYUBhc3NheXNAZGF0YUBsaXN0RGF0YVtbImxvZ2NvdW50cyJdXQ0KbGltbWEgPC0gcmJpbmQobGltbWEsIG5lZ19wcm9iZXNfc2F2ZSkNCmNvbG5hbWVzKGxpbW1hKSA8LSBjb2xuYW1lcyhhcy5kYXRhLmZyYW1lKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbInFfbm9ybSJdXSkpDQphc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAibGltbWEiKSA8LSAgbGltbWENCg0KcnV2NCA8LXJ1djRAYXNzYXlzQGRhdGFAbGlzdERhdGFbWyJsb2djb3VudHMiXV0NCnJ1djQgPC0gcmJpbmQocnV2NCwgbmVnX3Byb2Jlc19zYXZlKQ0KY29sbmFtZXMocnV2NCkgPC0gY29sbmFtZXMoYXMuZGF0YS5mcmFtZSh0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJxX25vcm0iXV0pKQ0KYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gInJ1djQiKSA8LSAgcnV2NA0KYGBgDQoNCiMjIENob29zZSBtZXRob2Qgey5hY3RpdmV9DQoNCmBgYHtyfQ0KIyBjaG9vc2UgbWV0aG9kIHRvIHJlcGxhY2UgcV9ub3JtIG9yIHNldCBpdCB0byAiIg0KbWV0aG9kIDwtICIiDQoNCiMgcmVwbGFjZSBxX25vcm0gd2l0aCBjaG9zZW4gbWV0aG9kDQppZiAoIW1ldGhvZCA9PSAiIikgew0KICAjIHNhdmUgb3JnaW5hbCBxX25vcm0NCiAgYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gIm9yaWdpbmFsIikgPC0gIGFzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9ICJxX25vcm0iKQ0KICANCiAgYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gInFfbm9ybSIpIDwtICBhc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSBtZXRob2QpDQogIHByaW50KHBhc3RlKCJCYXRjaCBjb3JyZWN0aW9uIG1ldGhvZDoiLCBtZXRob2QpKQ0KfSBlbHNlIHtwcmludCgiTm8gYmF0Y2ggY29ycmVjdGlvbiBuZWVkZWQiKX0NCmBgYA0KDQojIyBVbWFwIGFmdGVyIGJhdGNoIGNvcnJlY3Rpb24NCg0KYGBge3IsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTgsIGV2YWw9YmF0Y2hfY29ycmVjdGlvbl9uZWVkZWR9DQp1bWFwX291dCA8LQ0KICB1bWFwKHQobG9nMihhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhICwgZWx0ID0gInFfbm9ybSIpKSksDQogICAgICAgY29uZmlnID0gY3VzdG9tX3VtYXApDQpwRGF0YSh0YXJnZXRfRGF0YSlbLCBjKCJVTUFQMSIsICJVTUFQMiIpXSA8LSB1bWFwX291dCRsYXlvdXRbLCBjKDEsMildDQpnZ3Bsb3QocERhdGEodGFyZ2V0X0RhdGEpLA0KICAgICAgIGFlcyh4ID0gVU1BUDEsIHkgPSBVTUFQMiwgY29sb3IgPSBzbGlkZV9uYW1lLCBzaGFwZSA9IEFOTjEpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsNCiAgI2dlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9cm93Lm5hbWVzKHBEYXRhKHRhcmdldF9EYXRhKSkpLCBzaXplPTIsbWF4Lm92ZXJsYXBzID0gMTAwKSsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCiMgNi4zIFJ1biB0U05FIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNClVzZSB0aGUgdGFiLW1lbnUgdG8gbmF2aWdhdGUhDQoNCk9uZSBjb21tb24gYXBwcm9hY2ggdG8gdW5kZXJzdGFuZGluZyBoaWdoLXBsZXggZGF0YSBpcyBkaW1lbnNpb24NCnJlZHVjdGlvbi4gVHdvIGNvbW1vbiBtZXRob2RzIGFyZSBVTUFQIGFuZCB0U05FLCB3aGljaCBhcmUNCm5vbi1vcnRob2dvbmFsbHkgY29uc3RyYWluZWQgcHJvamVjdGlvbnMgdGhhdCBjbHVzdGVyIHNhbXBsZXMgYmFzZWQgb24NCm92ZXJhbGwgZ2VuZSBleHByZXNzaW9uLiBJbiB0aGlzIHN0dWR5LCB3ZSBzZWUgYnkgZWl0aGVyIFVNQVAgKGZyb20gdGhlDQp1bWFwIHBhY2thZ2UpIG9yIHRTTkUgKGZyb20gdGhlIFJ0c25lIHBhY2thZ2UpDQoNCiMjIDENCg0KYGBge3IgdFNORSwgZmlnLndpZHRoPTEwLGZpZy5oZWlnaHQ9OH0NCnRzbmVfb3V0IDwtDQogIFJ0c25lKHQobG9nMihhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhICwgZWx0ID0gInFfbm9ybSIpKSksDQogICAgICAgIHBlcnBsZXhpdHkgPSBuY29sKHRhcmdldF9EYXRhKSouMTUpDQpwRGF0YSh0YXJnZXRfRGF0YSlbLCBjKCJ0U05FMSIsICJ0U05FMiIpXSA8LSB0c25lX291dCRZWywgYygxLDIpXQ0KZ2dwbG90KHBEYXRhKHRhcmdldF9EYXRhKSwNCiAgICAgICBhZXMoeCA9IHRTTkUxLCB5ID0gdFNORTIsIHNoYXBlID0gc2xpZGVfbmFtZSwgY29sb3IgPSBBTk4yKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAzKSArDQogICNnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsPXJvdy5uYW1lcyhwRGF0YSh0YXJnZXRfRGF0YSkpKSwgc2l6ZT0yLG1heC5vdmVybGFwcyA9IDEwMCkrDQogIHRoZW1lX2J3KCkNCmBgYA0KDQojIyAyDQoNCmBgYHtyLCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD04fQ0KdHNuZV9vdXQgPC0NCiAgUnRzbmUodChsb2cyKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEgLCBlbHQgPSAicV9ub3JtIikpKSwNCiAgICAgICAgcGVycGxleGl0eSA9IG5jb2wodGFyZ2V0X0RhdGEpKi4xNSkNCnBEYXRhKHRhcmdldF9EYXRhKVssIGMoInRTTkUxIiwgInRTTkUyIildIDwtIHRzbmVfb3V0JFlbLCBjKDEsMildDQpnZ3Bsb3QocERhdGEodGFyZ2V0X0RhdGEpLA0KICAgICAgIGFlcyh4ID0gdFNORTEsIHkgPSB0U05FMiwgY29sb3IgPSBBTk4zLCBzaGFwZSA9IEFOTjEpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsNCiAgI2dlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9cm93Lm5hbWVzKHBEYXRhKHRhcmdldF9EYXRhKSkpLCBzaXplPTIsbWF4Lm92ZXJsYXBzID0gMTAwKSsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCiMgNi40IENsdXN0ZXJpbmcgaGlnaCBDViBHZW5lcyB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpBbm90aGVyIGFwcHJvYWNoIHRvIGV4cGxvcmUgdGhlIGRhdGEgaXMgdG8gY2FsY3VsYXRlIHRoZSBjb2VmZmljaWVudCBvZg0KdmFyaWF0aW9uICgkQ1YkKSBmb3IgZWFjaCBnZW5lICgkZyQpIHVzaW5nIHRoZSBmb3JtdWxhDQokQ1ZfZz1TRF9nL21lYW5fZyQuIFdlIHRoZW4gaWRlbnRpZnkgZ2VuZXMgd2l0aCBoaWdoIENWcyB0aGF0IHNob3VsZA0KaGF2ZSBsYXJnZSBkaWZmZXJlbmNlcyBhY3Jvc3MgdGhlIHZhcmlvdXMgcHJvZmlsZWQgc2VnbWVudHMuIFRoaXMNCnVuYmlhc2VkIGFwcHJvYWNoIGNhbiByZXZlYWwgaGlnaGx5IHZhcmlhYmxlIGdlbmVzIGFjcm9zcyB0aGUgc3R1ZHkuDQoNCldlIHBsb3QgdGhlIHJlc3VsdHMgdXNpbmcgdW5zdXBlcnZpc2VkIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLA0KZGlzcGxheWVkIGFzIGEgaGVhdG1hcC4NCg0KYGBge3IgY2x1c3RlcmluZzF9DQojIGNyZWF0ZSBhIGxvZzIgdHJhbnNmb3JtIG9mIHRoZSBkYXRhIGZvciBhbmFseXNpcw0KYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gImxvZ19xIikgPC0NCiAgYXNzYXlEYXRhQXBwbHkodGFyZ2V0X0RhdGEsIDIsIEZVTiA9IGxvZywgYmFzZSA9IDIsIGVsdCA9ICJxX25vcm0iKQ0KDQojIGNyZWF0ZSBDViBmdW5jdGlvbg0KY2FsY19DViA8LSBmdW5jdGlvbih4KSB7c2QoeCkgLyBtZWFuKHgpfQ0KQ1ZfZGF0IDwtIGFzc2F5RGF0YUFwcGx5KHRhcmdldF9EYXRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGVsdCA9ICJsb2dfcSIsIE1BUkdJTiA9IDEsIGNhbGNfQ1YpDQojIHNob3cgdGhlIGhpZ2hlc3QgQ0QgZ2VuZXMgYW5kIHRoZWlyIENWIHZhbHVlcw0Kc29ydChDVl9kYXQsIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjVdDQpgYGANCg0KIyMgVGFibGUgb2YgQ1YgdmFsdWVzDQoNCmBgYHtyfQ0KIyBzaG93IHRoZSBoaWdoZXN0IENEIGdlbmVzIGFuZCB0aGVpciBDViB2YWx1ZXMNCmRhdGF0YWJsZShhcy5kYXRhLmZyYW1lKENWX2RhdCksDQogICAgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgb3JkZXIgPSBsaXN0KDEsICdkZXNjJyksDQogICAgICAgICAgICBkb20gPSAnQmZ0cmlwJywNCiAgICAgICAgICAgIGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKQ0KICAgICAgICAgICksIGNhcHRpb24gPSAiQ1YgdmFsdWVzIG9mIGdlbmVzIiANCikgJT4lIGZvcm1hdFJvdW5kKGNvbHVtbnM9YygiQ1ZfZGF0IiksIGRpZ2l0cz0zKQ0KDQpgYGANCg0KIyMgSGVhdG1hcCBnZW5lcyBpbiB0aGUgdG9wIDNyZCBvZiB0aGUgQ1YgdmFsdWVzIHsuYWN0aXZlfQ0KDQpgYGB7ciBDVmhlYXRtYXAsIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTE1fQ0KR09JIDwtIG5hbWVzKENWX2RhdClbQ1ZfZGF0ID4gcXVhbnRpbGUoQ1ZfZGF0LCAwLjc1KV0NCg0KcGhlYXRtYXAoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVtHT0ksIF0sIGVsdCA9ICJsb2dfcSIpLA0KICAgICAgICAgc2NhbGUgPSAicm93IiwNCiAgICAgICAgIGN1dHJlZV9jb2xzID0gMywNCiAgICAgICAgIGN1dHJlZV9yb3dzID0gMiwNCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBGQUxTRSwgc2hvd19jb2xuYW1lcyA9IFRSVUUsDQogICAgICAgICBib3JkZXJfY29sb3IgPSBOQSwNCiAgICAgICAgIGRyb3BfbGV2ZWxzID0gVFJVRSwNCiAgICAgICAgIGNsdXN0ZXJpbmdfbWV0aG9kID0gImF2ZXJhZ2UiLA0KICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgICBicmVha3MgPSBzZXEoLTMsIDMsIDAuMDUpLA0KICAgICAgICAgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKGMoInB1cnBsZTMiLCAiYmxhY2siLCAieWVsbG93MiIpKSgxMjApLA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBjb2xvcl9saXN0LA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBwRGF0YSh0YXJnZXRfRGF0YSlbLCBhbm5fbmFtZXNdKQ0KDQpgYGANCg0KYGBge3IgbG9nX3RyYW5zZm9ybX0NCmFzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9ICJsb2dfcSIpIDwtICBhc3NheURhdGFBcHBseSh0YXJnZXRfRGF0YSwgMiwgRlVOID0gbG9nLCBiYXNlID0gMiwgZWx0ID0gInFfbm9ybSIpDQpsb2dfcSA8LWFzLmRhdGEuZnJhbWUoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSwgZWx0PSAibG9nX3EiKSkNCiNiYXRjaCA8LWFzLmRhdGEuZnJhbWUoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSwgZWx0PSAiYmF0Y2giKSkNCmBgYA0KDQojIDYuNS4wIENyZWF0ZSBzdWJzZXQgb2YgZGF0YQ0KDQpgYGB7ciBkZWZpbmVfYWN0aXZlX2FvaXN9DQojIGRldGVybWluZSBBT0lzIHRvIHVzZQ0KI2FjdGl2ZV9hb2lzPC1yb3duYW1lcyhhbm4pW2FubiRwYXRpZW50PT0icDQiXQ0KYWN0aXZlX2FvaXM8LSBuYW1lcyhhcy5kYXRhLmZyYW1lKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEsIGVsdD0gImV4cHJzIikpKQ0KYGBgDQoNCiMgNi41LjEgQ2x1c3RlcmluZyBoaWdoIENWIGdlbmVzIGZvciBzdWJzZXQgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KQ2FsY3VsYXRpbmcgQ1YgdmFsdWVzDQoNCmBgYHtyIGNsdXN0ZXJpbmdfc3Vic2V0LCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD0xNX0NCiMgY3JlYXRlIGEgbG9nMiB0cmFuc2Zvcm0gb2YgdGhlIGRhdGEgZm9yIGFuYWx5c2lzDQphc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAibG9nX3EiKSA8LQ0KICBhc3NheURhdGFBcHBseSh0YXJnZXRfRGF0YSwgMiwgRlVOID0gbG9nLCBiYXNlID0gMiwgZWx0ID0gInFfbm9ybSIpDQoNCiMgY3JlYXRlIENWIGZ1bmN0aW9uDQpjYWxjX0NWIDwtIGZ1bmN0aW9uKHgpIHtzZCh4KSAvIG1lYW4oeCl9DQpDVl9kYXQgPC0gYXNzYXlEYXRhQXBwbHkodGFyZ2V0X0RhdGFbLGFjdGl2ZV9hb2lzXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBlbHQgPSAibG9nX3EiLCBNQVJHSU4gPSAxLCBjYWxjX0NWKQ0KDQpgYGANCg0KIyMgVGFibGUgb2YgQ1YgdmFsdWVzDQoNCmBgYHtyfQ0KIyBzaG93IHRoZSBoaWdoZXN0IENEIGdlbmVzIGFuZCB0aGVpciBDViB2YWx1ZXMNCmRhdGF0YWJsZShhcy5kYXRhLmZyYW1lKENWX2RhdCksDQogICAgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgb3JkZXIgPSBsaXN0KDEsICdkZXNjJyksDQogICAgICAgICAgICBkb20gPSAnQmZ0cmlwJywNCiAgICAgICAgICAgIGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKQ0KICAgICAgICAgICksIGNhcHRpb24gPSAiQ1YgdmFsdWVzIG9mIGdlbmVzIiANCikgJT4lIGZvcm1hdFJvdW5kKGNvbHVtbnM9YygiQ1ZfZGF0IiksIGRpZ2l0cz0zKQ0KDQpgYGANCg0KIyMgSGVhdG1hcCBvbiBvZiBzdWJzZXQsIGdlbmVzIGluIHRoZSB0b3AgM3JkIG9mIHRoZSBDViB2YWx1ZXMgey5hY3RpdmV9DQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xNX0NCiMgSWRlbnRpZnkgZ2VuZXMgaW4gdGhlIHRvcCAzcmQgb2YgdGhlIENWIHZhbHVlcw0KR09JIDwtIG5hbWVzKENWX2RhdClbQ1ZfZGF0ID4gcXVhbnRpbGUoQ1ZfZGF0LCAwLjc1KV0NCnBoZWF0bWFwKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGFbR09JLGFjdGl2ZV9hb2lzIF0sIGVsdCA9ICJsb2dfcSIpLA0KICAgICAgICBzY2FsZSA9ICJyb3ciLA0KICAgICAgICBmb250c2l6ZV9yb3cgPSA1LA0KICAgICAgICBjdXRyZWVfY29scyA9IDMsDQogICAgICAgIGN1dHJlZV9yb3dzID0gMiwNCiAgICAgICAgc2hvd19yb3duYW1lcyA9IEZBTFNFLCBzaG93X2NvbG5hbWVzID0gVFJVRSwNCiAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEsDQogICAgICAgIGNsdXN0ZXJpbmdfbWV0aG9kID0gImF2ZXJhZ2UiLA0KICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICBicmVha3MgPSBzZXEoLTMsIDMsIDAuMDUpLA0KICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygicHVycGxlMyIsICJibGFjayIsICJ5ZWxsb3cyIikpKDEyMCksDQogICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBjb2xvcl9saXN0LA0KICAgICAgICBhbm5vdGF0aW9uX2NvbCA9DQogICAgICAgICAgcERhdGEodGFyZ2V0X0RhdGEpWywgYW5uX25hbWVzXSkNCmBgYA0KDQojIDcuMSBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbg0KDQohW10oTDovcGtsb29zdGVybWFuL0tpZG5leV9vcmdhbl9hdGxhcy9raWRuZXlfc3RydWN0dXJlLnBuZykNCg0KIyMgdC10ZXN0DQoNCmBgYHtyIHR0ZXN0fQ0KI2djKCkNCnBsb3RzPC1saXN0KCkNCnRhYmxlczwtbGlzdCgpDQpsYWJlbHM8LWxpc3QoKQ0KdGVzdDwtInR0ZXN0Ig0KbXRjPC0iQlkiDQojb3B0aW9uczogImhvbG0iICAgICAgICJob2NoYmVyZyIgICAiaG9tbWVsIiAgICAgImJvbmZlcnJvbmkiICJCSCIgICAgICAgICAiQlkiICAgICAgICAgImZkciIgDQpjb3VudGVyPTENCg0KY29tcHNfZGY8LWRhdGEuZnJhbWUoY29tcD0nJyx2YWw9JycpDQoNCmZvcihyZWdpb24gaW4gdW5pcXVlKHBEYXRhKHRhcmdldF9EYXRhKSRBTk4zKSkgew0KICBmb3IgKGFjdGl2ZV9ncm91cDEgaW4gdW5pcXVlKHBEYXRhKHRhcmdldF9EYXRhKSRBTk4xKSkgew0KICAgIGZvciAoYWN0aXZlX2dyb3VwMiBpbiB1bmlxdWUocERhdGEodGFyZ2V0X0RhdGEpJEFOTjEpKSB7DQogICAgICANCiAgICAgICNzdXByZXNzIHJlZHVuY2FudCBjb21wYXJlcw0KICAgICAgaWYoYWN0aXZlX2dyb3VwMT09YWN0aXZlX2dyb3VwMikge25leHR9DQogICAgICBjb21wPC1wYXN0ZShzb3J0KGMocmVnaW9uLCBhY3RpdmVfZ3JvdXAxLGFjdGl2ZV9ncm91cDIpKSxjb2xsYXBzZSA9ICJfIikNCiAgICAgIGlmKGNvbXAgJWluJSBjb21wc19kZiRjb21wKSB7bmV4dH0NCiAgICAgIHRlbXBfZGY8LWRhdGEuZnJhbWUoY29tcD1jb21wICx2YWw9MSkNCiAgICAgIGNvbXBzX2RmPC1yYmluZChjb21wc19kZix0ZW1wX2RmKQ0KICAgICAgDQogICAgICBsYWJlbHNbW2NvdW50ZXJdXTwtcGFzdGUoYWN0aXZlX2dyb3VwMSwiIHZzICIsIGFjdGl2ZV9ncm91cDIpDQogICAgICBncm91cDE8LWxvZ19xWyxuYW1lcyhhcy5kYXRhLmZyYW1lKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEsIGVsdD0gImV4cHJzIikpKVtwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMT09YWN0aXZlX2dyb3VwMSAmIHBEYXRhKHRhcmdldF9EYXRhKSRBTk4zPT1yZWdpb25dXQ0KICAgICAgZ3JvdXAyPC1sb2dfcVssbmFtZXMoYXMuZGF0YS5mcmFtZShhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhLCBlbHQ9ICJleHBycyIpKSlbcERhdGEodGFyZ2V0X0RhdGEpJEFOTjE9PWFjdGl2ZV9ncm91cDIgJiBwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMz09cmVnaW9uXV0NCiAgICAgIA0KICAgICAgI3J1biB0X3Rlc3RzICANCiAgICAgIHJlc3VsdHM8LWFzLmRhdGEuZnJhbWUgKCBhcHBseShsb2dfcSwgMSwgZnVuY3Rpb24oeCkgdC50ZXN0KHhbY29sbmFtZXMoZ3JvdXAxKV0seFtjb2xuYW1lcyhncm91cDIpXSkkcC52YWx1ZSkgKQ0KICAgICAgY29sbmFtZXMocmVzdWx0cyk8LSJyYXdfcF92YWx1ZSINCiAgICAgIA0KICAgICAgI211bHRpcGxlX3Rlc3RpbmdfY29ycmVjdGlvbg0KICAgICAgYWRqX3BfdmFsdWU8LSBwLmFkanVzdChyZXN1bHRzJHJhd19wX3ZhbHVlLG1ldGhvZD1tdGMpDQogICAgICByZXN1bHRzPC1jYmluZChyZXN1bHRzLGFkal9wX3ZhbHVlKQ0KICAgICAgDQogICAgICAjY2FsY19mZHINCiAgICAgIEZEUjwtIHAuYWRqdXN0KHJlc3VsdHMkcmF3X3BfdmFsdWUsbWV0aG9kPSJmZHIiKQ0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxGRFIpDQogICAgICANCiAgICAgICNmb2xkX2NoYW5nZXMNCiAgICAgICNhcyBiYXNlIGRhdGEgaXMgYWxyZWFkeSBsb2cgdHJhbnNmb3JtZWQsIG1lYW5zIG5lZWQgdG8gYmUgc3VidHJhY3RlZCB0byBnZXQgRkMgaW4gbG9nIHNwYWNlDQogICAgICBmY2hhbmdlczwtYXMuZGF0YS5mcmFtZShhcHBseShsb2dfcSwgMSwgZnVuY3Rpb24oeCkgKG1lYW4oeFtjb2xuYW1lcyhncm91cDEpXSkgLSBtZWFuKHhbY29sbmFtZXMoZ3JvdXAyKV0pKSkpDQogICAgICBjb2xuYW1lcyhmY2hhbmdlcyk8LSJGQyINCiAgICAgICNwYXN0ZSgiRkMiLGFjdGl2ZV9ncm91cDEsIiAvICIsYWN0aXZlX2dyb3VwMikNCiAgICAgIHJlc3VsdHM8LWNiaW5kKHJlc3VsdHMsZmNoYW5nZXMpDQogICAgICANCiAgICAgICNhZGQgZ2VuZW5hbWVzDQogICAgICByZXN1bHRzJEdlbmU8LXJvd25hbWVzKHJlc3VsdHMpDQogICAgICANCiAgICAgICNzZXQgY2F0ZWdvcmllcyBiYXNlZCBvbiBQLXZhbHVlICYgRkRSIGZvciBwbG90dGluZw0KICAgICAgcmVzdWx0cyRDb2xvciA8LSAiTlMgb3IgRkMgPCAxIg0KICAgICAgcmVzdWx0cyRDb2xvcltyZXN1bHRzJHJhd19wX3ZhbHVlIDwgMC4wNV0gPC0gIlAgPCAwLjA1Ig0KICAgICAgcmVzdWx0cyRDb2xvcltyZXN1bHRzJEZEUiA8IDAuMDVdIDwtICJGRFIgPCAwLjA1Ig0KICAgICAgcmVzdWx0cyRDb2xvcltyZXN1bHRzJEZEUiA8IDAuMDAxXSA8LSAiRkRSIDwgMC4wMDEiDQogICAgICByZXN1bHRzJENvbG9yW2FicyhyZXN1bHRzJEZDKSA8MV0gPC0gIk5TIG9yIEZDIDwgMSINCiAgICAgIHJlc3VsdHMkQ29sb3IgPC0gZmFjdG9yKHJlc3VsdHMkQ29sb3IsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJOUyBvciBGQyA8IDEiLCAiUCA8IDAuMDUiLCAiRkRSIDwgMC4wNSIsICJGRFIgPCAwLjAwMSIpKQ0KICAgICAgDQogICAgICAjdnVsY2Fub3Bsb3QNCiAgICAgIA0KICAgICAgIyBwaWNrIHRvcCBnZW5lcyBmb3IgZWl0aGVyIHNpZGUgb2Ygdm9sY2FubyB0byBsYWJlbA0KICAgICAgIyBvcmRlciBnZW5lcyBmb3IgY29udmVuaWVuY2U6DQogICAgICANCiAgICAgIHJlc3VsdHMkaW52ZXJ0X1AgPC0gKC1sb2cxMChyZXN1bHRzJGFkal9wX3ZhbHVlKSkgKiBzaWduKHJlc3VsdHMkRkMpDQogICAgICB0b3BfZyA8LSBjKCkNCiAgICAgIHRvcF9nIDwtIGModG9wX2csDQogICAgICAgICAgICAgICAgIHJlc3VsdHNbLCAnR2VuZSddWw0KICAgICAgICAgICAgICAgICAgIG9yZGVyKHJlc3VsdHNbLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MjBdXSwNCiAgICAgICAgICAgICAgICAgcmVzdWx0c1ssICdHZW5lJ11bb3JkZXIocmVzdWx0c1ssICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gRkFMU0UpWzE6MjBdXSkNCiAgICAgIHRvcF9nPC0gdW5pcXVlKHRvcF9nKQ0KICAgICAgcmVzdWx0cyA8LSByZXN1bHRzWywgLTEqbmNvbChyZXN1bHRzKV0gIyByZW1vdmUgaW52ZXJ0X1AgZnJvbSBtYXRyaXgNCiAgICAgIA0KICAgICAgIyBHcmFwaCByZXN1bHRzDQogICAgICANCiAgICAgIHBsb3RzW1tjb3VudGVyXV08LSBnZ3Bsb3QocmVzdWx0cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBGQywgeSA9IC1sb2cxMChyYXdfcF92YWx1ZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IENvbG9yLCBsYWJlbCA9IEdlbmUpKSArDQogICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoMSwgLTEpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAoMC4wNSksIGx0eSA9ICJkYXNoZWQiKSArDQogICAgICAgIGdlb21fcG9pbnQoKSArDQogICAgICAgIGxhYnMoeCA9IHBhc3RlKCJFbnJpY2hlZCBpbiIsIGFjdGl2ZV9ncm91cDIsIiA8LSBsb2cyKEZDKSAtPiBFbnJpY2hlZCBpbiIsIGFjdGl2ZV9ncm91cDEpLA0KICAgICAgICAgICAgIHkgPSAiU2lnbmlmaWNhbmNlLCAtbG9nMTAoUCkiLA0KICAgICAgICAgICAgIGNvbG9yID0gIlNpZ25pZmljYW5jZSIpICsNCiAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoYEZEUiA8IDAuMDAxYCA9ICJkb2RnZXJibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYEZEUiA8IDAuMDVgID0gImxpZ2h0Ymx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBQIDwgMC4wNWAgPSAib3JhbmdlMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBOUyBvciBGQyA8IDAuNWAgPSAiZ3JheSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gNCkpKSArDQogICAgICAgIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwwLjA1KSkpICsNCiAgICAgICAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBzdWJzZXQocmVzdWx0cywgKC0xPkZDfCBGQz4xKSAmIEZEUiA8IDAuMDUgJiBHZW5lICVpbiUgdG9wX2cpLA0KICAgICAgICAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IDAuMTUsIGNvbG9yID0gImJsYWNrIiwgc2l6ZT0zLjUsDQogICAgICAgICAgICAgICAgICAgICAgICBtaW4uc2VnbWVudC5sZW5ndGggPSAuMSwgYm94LnBhZGRpbmcgPSAuMiwgbHdkID0gMiwNCiAgICAgICAgICAgICAgICAgICAgICAgIG1heC5vdmVybGFwcyA9IDUwKSArDQogICAgICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDEwKSArDQogICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogICAgICAgIGdndGl0bGUocGFzdGUoImNsYXNzOiAiLCByZWdpb24sIiAtICIsIHRlc3QsIG10YywibXVsdGl0ZXN0IGNvcnIiKSkNCiAgICAgIA0KICAgICAgI3N0b3JlIHRhYmxlcyBmb3IgZGlzcGxheSBsYXRlcg0KICAgICAgdGFibGVzW1tjb3VudGVyXV08LXJlc3VsdHMNCiAgICAgIA0KICAgICAgY291bnRlciA9IGNvdW50ZXIrMQ0KICAgICAgI2RhdGF0YWJsZShzdWJzZXQocmVzdWx0cywgR2VuZSAlaW4lIEdPSSksIHJvd25hbWVzPUZBTFNFLGNhcHRpb24gPSBwYXN0ZSgiREUgcmVzdWx0cyAiLCBhY3RpdmVfZ3JvdXAxLCIgdnMgIiwgYWN0aXZlX2dyb3VwMikpDQogICAgfQ0KICB9DQp9DQpncmlkLmFycmFuZ2UoZ3JvYnM9cGxvdHMsbmNvbD0yKQ0KYGBgDQoNCmBgYHtyIGR1bXBfdGFibGVzLHJlc3VsdHM9J2FzaXMnfQ0KI3N0cmFuZ2x5IGRvZXMgbm90IGFwcGVhciBpbiBodG1sIG91dHB1dCAtPiBjaHVja3MgYXJlIGxpbWl0ZWQgdG8gb25lIGRhdGF0YWJsZSBwZXIgY2h1Y2suIFRoZSBodG1sdG9vbHMgYXJlIGEgd2F5IGFyb3VuZCB0aGlzIGJ1dCBkb2VzIHNob3cgdGhlbSBzdGFja2VkIGluIGEgYm94Lg0KcmVzdWx0c190YWJsZXMgPC0gaHRtbHRvb2xzOjp0YWdMaXN0KCkNCmZvciAoYyBpbiAoMjpjb3VudGVyLTEpKSB7DQogICNHZW5lICVpbiUgR09JDQogIHJlc3VsdHNfdGFibGVzW1tjXV0gPC0gZGF0YXRhYmxlKHN1YnNldCh0YWJsZXNbW2NdXSwgQ29sb3IgPT0gYygiRkRSIDwgMC4wMDEiLCAgIlAgPCAwLjA1IikpLCANCiAgICAgICAgICAgcm93bmFtZXM9RkFMU0UsDQogICAgICAgICAgIGV4dGVuc2lvbnMgPSBjKCdCdXR0b25zJyksIG9wdGlvbnMgPSBsaXN0ICgNCiAgICAgICAgICAgICAgZG9tID0gJ0JmdHJpcCcsDQogICAgICAgICAgICAgIGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKQ0KICAgICAgICAgICAgKSwgaGVpZ2h0ID0gNTUwLA0KICAgICAgICAgICBjYXB0aW9uID0gcGFzdGUoIkRFIHJlc3VsdHMgIiwgbGFiZWxzW1tjXV0pLGZpbHRlcj0ndG9wJykgJT4lIGZvcm1hdFJvdW5kKGNvbHVtbnM9YygicmF3X3BfdmFsdWUiLCJhZGpfcF92YWx1ZSIsIkZEUiIsIkZDIiksIGRpZ2l0cz0zKQ0KICAjY2F0KCdcblxuPCEtLSAtLT5cblxuJykNCn0gICAgICAgICAgICANCnJlc3VsdHNfdGFibGVzDQpgYGANCg0KYGBge3J9DQpwcmludCgiIikgIyBmb3IgbGF5b3V0IG9mIG5leHQgc2VjdGlvbg0KYGBgDQoNCiMgNy4yIERFIGFuYWx5c2lzIHdpdGggTE1NDQoNCkEgY29tbW9uIHN0YXRpc3RpY2FsIGFwcHJvYWNoIGlzIHRvIHVzZSBhIGxpbmVhciBtaXhlZC1lZmZlY3QgbW9kZWwNCihMTU0pLiBUaGUgTE1NIGFsbG93cyB0aGUgdXNlciB0byBhY2NvdW50IGZvciB0aGUgc3Vic2FtcGxpbmcgcGVyDQp0aXNzdWU7IGluIG90aGVyIHdvcmRzLCB3ZSBhZGp1c3QgZm9yIHRoZSBmYWN0IHRoYXQgdGhlIG11bHRpcGxlIHJlZ2lvbnMNCm9mIGludGVyZXN0IHBsYWNlZCBwZXIgdGlzc3VlIHNlY3Rpb24gYXJlIG5vdCBpbmRlcGVuZGVudCBvYnNlcnZhdGlvbnMsDQphcyBpcyB0aGUgYXNzdW1wdGlvbiB3aXRoIG90aGVyIHRyYWRpdGlvbmFsIHN0YXRpc3RpY2FsIHRlc3RzLiBUaGUNCmZvcm11bGF0aW9uIG9mIHRoZSBMTU0gbW9kZWwgZGVwZW5kcyBvbiB0aGUgc2NpZW50aWZpYyBxdWVzdGlvbiBiZWluZw0KYXNrZWQuDQoNCk92ZXJhbGwsIHRoZXJlIGFyZSB0d28gZmxhdm9ycyBvZiB0aGUgTE1NIG1vZGVsIHdoZW4gdXNlZCB3aXRoIEdlb014DQpkYXRhOiBpKSB3aXRoIGFuZCBpaSkgd2l0aG91dCByYW5kb20gc2xvcGUuDQoNCldoZW4gY29tcGFyaW5nIGZlYXR1cmVzIHRoYXQgY28tZXhpc3QgaW4gYSBnaXZlbiB0aXNzdWUgc2VjdGlvbiwgYQ0KcmFuZG9tIHNsb3BlIGlzIGluY2x1ZGVkIGluIHRoZSBMTU0gbW9kZWwuIFdoZW4gY29tcGFyaW5nIGZlYXR1cmVzIHRoYXQNCmFyZSBtdXR1YWxseSBleGNsdXNpdmUgaW4gYSBnaXZlbiB0aXNzdWUgc2VjdGlvbiB0aGUgTE1NIG1vZGVsIGRvZXMgbm90DQpyZXF1aXJlIGEgcmFuZG9tIHNsb3BlLg0KDQpNb3N0bHksIHdlIHVzZSBwYXRpZW50L3NhbXBsZSBhcyBSYW5kb20gSW50ZXJjZXB0IHdoZW4gdGhleSBhcmUgY29tYmluZWQNCm9uIHNsaWRlcy4NCg0KIVtdKGh0dHA6Ly91c2VxLm5sL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDIyLzEyL0xNTV9zZXR1cC5wbmcpDQoNCmBgYHtyIExNTSByZWdpb259DQojIGNvbnZlcnQgdGVzdCB2YXJpYWJsZXMgdG8gZmFjdG9ycw0KcERhdGEodGFyZ2V0X0RhdGEpJHRlc3RDbGFzcyA8LSBmYWN0b3IocERhdGEodGFyZ2V0X0RhdGEpJEFOTjEsIHVuaXF1ZSh0YXJnZXRfRGF0YSRBTk4xKSkNCnBEYXRhKHRhcmdldF9EYXRhKVtbInNsaWRlIl1dIDwtIGZhY3RvcihwRGF0YSh0YXJnZXRfRGF0YSlbWyJzbGlkZV9uYW1lIl1dKQ0KI2Fzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9ICJsb2dfcSIpIDwtIGFzc2F5RGF0YUFwcGx5KHRhcmdldF9EYXRhLCAyLCBGVU4gPSBsb2csIGJhc2UgPSAyLCBlbHQgPSAicV9ub3JtIikNCg0KIyBydW4gTE1NOg0KIyBmb3JtdWxhIGZvbGxvd3MgY29udmVudGlvbnMgZGVmaW5lZCBieSB0aGUgbG1lNCBwYWNrYWdlDQpsbW1fcmVzdWx0cyA8LSBjKCkNCmZvciAocmVnaW9uIGluIHVuaXF1ZShwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMykpIHsNCiAgICBpbmQgPC0gcERhdGEodGFyZ2V0X0RhdGEpJEFOTjMgPT0gcmVnaW9uDQoNCiAgICBtaXhlZE91dG1jIDwtDQogICAgICAgIG1peGVkTW9kZWxERSh0YXJnZXRfRGF0YVssaW5kXSwNCiAgICAgICAgICAgICAgICAgICAgIGVsdCA9ICJsb2dfcSIsDQogICAgICAgICAgICAgICAgICAgICBtb2RlbEZvcm11bGEgPSB+IHRlc3RDbGFzcyArICgxIHwgc2xpZGUpLA0KICAgICAgICAgICAgICAgICAgICAgZ3JvdXBWYXIgPSAidGVzdENsYXNzIiwNCiAgICAgICAgICAgICAgICAgICAgIG5Db3JlcyA9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpLA0KICAgICAgICAgICAgICAgICAgICAgbXVsdGlDb3JlID0gVFJVRSkNCg0KICAgICMgZm9ybWF0IHJlc3VsdHMgYXMgZGF0YS5mcmFtZQ0KICAgIHJfdGVzdCA8LSBkby5jYWxsKHJiaW5kLCBtaXhlZE91dG1jWyJsc21lYW5zIiwgXSkNCiAgICB0ZXN0cyA8LSByb3duYW1lcyhyX3Rlc3QpDQogICAgcl90ZXN0IDwtIGFzLmRhdGEuZnJhbWUocl90ZXN0KQ0KICAgIHJfdGVzdCRDb250cmFzdCA8LSB0ZXN0cw0KDQogICAgIyB1c2UgbGFwcGx5IGluIGNhc2UgeW91IGhhdmUgbXVsdGlwbGUgbGV2ZWxzIG9mIHlvdXIgdGVzdCBmYWN0b3IgdG8NCiAgICAjIGNvcnJlY3RseSBhc3NvY2lhdGUgZ2VuZSBuYW1lIHdpdGggaXQncyByb3cgaW4gdGhlIHJlc3VsdHMgdGFibGUNCiAgICByX3Rlc3QkR2VuZSA8LQ0KICAgICAgICB1bmxpc3QobGFwcGx5KGNvbG5hbWVzKG1peGVkT3V0bWMpLA0KICAgICAgICAgICAgICAgICAgICAgIHJlcCwgbnJvdyhtaXhlZE91dG1jWyJsc21lYW5zIiwgXVtbMV1dKSkpDQogICAgcl90ZXN0JFN1YnNldCA8LSByZWdpb24NCiAgICByX3Rlc3QkRkRSIDwtIHAuYWRqdXN0KHJfdGVzdCRgUHIoPnx0fClgLCBtZXRob2QgPSAiZmRyIikNCiAgICByX3Rlc3QgPC0gcl90ZXN0WywgYygiR2VuZSIsICJTdWJzZXQiLCAiQ29udHJhc3QiLCAiRXN0aW1hdGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJQcig+fHR8KSIsICJGRFIiKV0NCiAgICANCiAgICBsbW1fcmVzdWx0cyA8LSByYmluZChsbW1fcmVzdWx0cywgcl90ZXN0KQ0KfQ0KYGBgDQoNCiMgNy4zIFZ1bGNhbm9wbG90ICsgdGFibGUgb2YgTE1NDQoNCmBgYHtyIGxtbV92dWxjYW5vLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMH0NCiMgQ2F0ZWdvcml6ZSBSZXN1bHRzIGJhc2VkIG9uIFAtdmFsdWUgJiBGRFIgZm9yIHBsb3R0aW5nDQpmY190aHJlc2hvbGQgPSAxDQoNCmxtbV9yZXN1bHRzJENvbG9yIDwtIHBhc3RlKCJOUyBvciBGQyA8ICIsZmNfdGhyZXNob2xkID0gMSkNCmxtbV9yZXN1bHRzJENvbG9yW2xtbV9yZXN1bHRzJGBQcig+fHR8KWAgPCAwLjA1XSA8LSAiUCA8IDAuMDUiDQpsbW1fcmVzdWx0cyRDb2xvcltsbW1fcmVzdWx0cyRGRFIgPCAwLjA1XSA8LSAiRkRSIDwgMC4wNSINCmxtbV9yZXN1bHRzJENvbG9yW2xtbV9yZXN1bHRzJEZEUiA8IDAuMDAxXSA8LSAiRkRSIDwgMC4wMDEiDQpsbW1fcmVzdWx0cyRDb2xvclthYnMobG1tX3Jlc3VsdHMkRXN0aW1hdGUpIDwgZmNfdGhyZXNob2xkXSA8LSBwYXN0ZSgiTlMgb3IgRkMgPCAiLGZjX3RocmVzaG9sZCA9IDEpDQpsbW1fcmVzdWx0cyRDb2xvciA8LSBmYWN0b3IobG1tX3Jlc3VsdHMkQ29sb3IsDQogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJOUyBvciBGQyA8IDEiLCAiUCA8IDAuMDUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRkRSIDwgMC4wNSIsICJGRFIgPCAwLjAwMSIpKQ0KY291bnRlciA9IDENCnBsb3RzX2xtbSA8LSBsaXN0KCkNCmZvcihjIGluIHVuaXF1ZShsbW1fcmVzdWx0cyRTdWJzZXQpICkgew0KICBsbW1fcmVzdWx0c19zbGljZSA9IGxtbV9yZXN1bHRzW2xtbV9yZXN1bHRzJFN1YnNldCA9PSBjLF0NCg0KICAjIHBpY2sgdG9wIGdlbmVzIGZvciBlaXRoZXIgc2lkZSBvZiB2b2xjYW5vIHRvIGxhYmVsDQogICMgb3JkZXIgZ2VuZXMgZm9yIGNvbnZlbmllbmNlOg0KICBsbW1fcmVzdWx0c19zbGljZSRpbnZlcnRfUCA8LSAoLWxvZzEwKGxtbV9yZXN1bHRzX3NsaWNlJGBQcig+fHR8KWApKSAqIHNpZ24obG1tX3Jlc3VsdHNfc2xpY2UkRXN0aW1hdGUpDQogIA0KICAjbG9vcCBoZXJlIG92ZXIgdGVzdGVkIGNvbmRpdGlvbnMgaWYgYXBwbGljYWJsZQ0KICB0b3BfZyA8LSBjKCkNCiAgdG9wX2cgPC0gYyh0b3BfZywNCiAgICBsbW1fcmVzdWx0c19zbGljZVssICdHZW5lJ11bDQogICAgICBvcmRlcihsbW1fcmVzdWx0c19zbGljZVssICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gVFJVRSlbMToxNV1dLA0KICAgIGxtbV9yZXN1bHRzX3NsaWNlWywgJ0dlbmUnXVsNCiAgICAgIG9yZGVyKGxtbV9yZXN1bHRzX3NsaWNlWywgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMToxNV1dKQ0KDQogIHRvcF9nIDwtIHVuaXF1ZSh0b3BfZykNCiAgDQogIGxtbV9yZXN1bHRzX3NsaWNlIDwtIGxtbV9yZXN1bHRzX3NsaWNlWywgLTEqbmNvbChsbW1fcmVzdWx0c19zbGljZSldICMgcmVtb3ZlIGludmVydF9QIGZyb20gbWF0cml4DQogIA0KICAjIEZsaXAgQ29udHJhc3QNCiAgY29udHJhc3RfbGFiIDwtIGFzLmNoYXJhY3RlcihsbW1fcmVzdWx0c19zbGljZSRDb250cmFzdCkNCiAgY29udHJhc3RfbGFiIDwtIHN0cnNwbGl0KGNvbnRyYXN0X2xhYiwgIi0iKVtbMV1dDQogIGNvbnRyYXN0X2xhYiA8LSBwYXN0ZShjb250cmFzdF9sYWJbMl0sICItIiwgY29udHJhc3RfbGFiWzFdKQ0KICANCiAgIyBHcmFwaCByZXN1bHRzDQogIHBsb3RzX2xtbVtbY291bnRlcl1dIDwtIGdncGxvdChsbW1fcmVzdWx0c19zbGljZSwNCiAgICAgICAgICAgICAgIGFlcyh4ID0gRXN0aW1hdGUsIHkgPSAtbG9nMTAoYFByKD58dHwpYCksDQogICAgICAgICAgICAgICAgICAgY29sb3IgPSBDb2xvciwgbGFiZWwgPSBHZW5lKSkgKw0KICAgICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoZmNfdGhyZXNob2xkLCAtZmNfdGhyZXNob2xkKSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgICAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAoMC4wNSksIGx0eSA9ICJkYXNoZWQiKSArDQogICAgICAgICAgZ2VvbV9wb2ludCgpICsNCiAgICAgICAgICBsYWJzKA0KICAgICAgICAgICAgeCA9IHBhc3RlKGNvbnRyYXN0X2xhYiwgIiBsb2cyKEZDKSIpLA0KICAgICAgICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICAgICAgICBjb2xvciA9ICJTaWduaWZpY2FuY2UiKSArDQogICAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoYEZEUiA8IDAuMDAxYCA9ICJkb2RnZXJibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgUCA8IDAuMDVgID0gIm9yYW5nZTIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBOUyBvciBGQyA8IDFgID0gImdyYXkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gNCkpKSArDQogICAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMDUpKSkgKw0KICAgICAgICAgIGdlb21fdGV4dF9yZXBlbChkYXRhID0gc3Vic2V0KGxtbV9yZXN1bHRzX3NsaWNlLCAgYFByKD58dHwpYCA8IDAuMDAxICYgYWJzKGxtbV9yZXN1bHRzX3NsaWNlJEVzdGltYXRlKSA+IGZjX3RocmVzaG9sZCAmIEdlbmUgJWluJSB0b3BfZyksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50LnBhZGRpbmcgPSAwLjE1LCBjb2xvciA9ICJibGFjayIsc2l6ZT01LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uc2VnbWVudC5sZW5ndGggPSAuMSwgYm94LnBhZGRpbmcgPSAuMiwgbHdkID0gMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4Lm92ZXJsYXBzID0gNTApICsNCiAgICAgICAgICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNikgKw0KICAgICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogICAgICAgICAgZmFjZXRfd3JhcCh+U3Vic2V0LCBzY2FsZXMgPSAiZnJlZV95IikNCiAgY291bnRlciA8LSBjb3VudGVyICsgMQ0KfQ0KZ3JpZC5hcnJhbmdlKGdyb2JzPXBsb3RzX2xtbSxuY29sPTIpDQpgYGAgICAgDQoNCmBgYHtyIHRhYmxlX29mX0xNTV9yZXN1bHRzIHJlZ2lvbn0NCiNzdWJzZXQobG1tX3Jlc3VsdHMsIEdlbmUgJWluJSBHT0kpDQpkYXRhdGFibGUobG1tX3Jlc3VsdHMsIHJvd25hbWVzID0gRkFMU0UsDQogICAgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgICBkb20gPSAnQmZ0cmlwJywNCiAgICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpDQogICAgICAgICAgICApLA0KICAgICAgICAgIGNhcHRpb24gPSAiREUgcmVzdWx0cyBmb3IgR2VuZXMgb2YgSW50ZXJlc3QgKD43NSUgQ1YpIixmaWx0ZXI9J3RvcCcpICU+JSBmb3JtYXRSb3VuZChjb2x1bW5zPWMoIkVzdGltYXRlIiwiUHIoPnx0fCkiLCJGRFIiKSwgZGlnaXRzPTMpDQpgYGANCg0KIyA3LjQgUGxvdHRpbmcgR2VuZXMgb2YgSW50ZXJlc3QNCg0KYGBge3IgcGxvdF9nZW5lX29mX2ludGVyZXN0LCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD01fQ0KbXlfZ29pcyA8LXVuaXF1ZShzdWJzZXQobG1tX3Jlc3VsdHMsIGBGRFJgIDwgMC4wMDEpJEdlbmUpDQp0bXBfdGJsPC1zdWJzZXQobG1tX3Jlc3VsdHMsIEdlbmUgJWluJSBteV9nb2lzKQ0KDQpteV9nb2lzIDwtIGMoKQ0KZm9yIChjb250cmFzdCBpbiB1bmlxdWUodG1wX3RibCRDb250cmFzdCkpIHsgIyBnZW5lIG9mIGludGVyZXN0IGZvciBhbGwgY29udHJhc3RzDQojZm9yIChjb250cmFzdCBpbiBjKCJKdXhfR2xvIC0gUHJvX1R1YiIpKSB7DQogIGluZCA8LSB0bXBfdGJsJENvbnRyYXN0ID09IGNvbnRyYXN0DQogIGdvaSA8LSB0bXBfdGJsW2luZCwgIkdlbmUiXVtvcmRlcih0bXBfdGJsW2luZCwgIkVzdGltYXRlIl0sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMTozXV0NCiAgbXlfZ29pcyA8LSBjKG15X2dvaXMsIGdvaSkNCiAgZ29pIDwtIHRtcF90YmxbaW5kLCAiR2VuZSJdW29yZGVyKHRtcF90YmxbaW5kLCAiRXN0aW1hdGUiXSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6M11dDQogIG15X2dvaXMgPC0gYyhteV9nb2lzLCBnb2kpDQp9DQoNCmlmKG5yb3codG1wX3RibCkgPiAxKSB7IA0KICBkYXRhdGFibGUodG1wX3RibCxyb3duYW1lcyA9IEZBTFNFLGNhcHRpb24gPSAiREUgcmVzdWx0cyBmb3IgR2VuZXMgb2YgSW50ZXJlc3QgIixmaWx0ZXI9J3RvcCcpICU+JSBmb3JtYXRSb3VuZChjb2x1bW5zPWMoIkVzdGltYXRlIiwiUHIoPnx0fCkiLCJGRFIiKSwgZGlnaXRzPTMpDQogDQpmb3IgKG15X2dvaSBpbiBteV9nb2lzKSB7DQojIHNob3cgZXhwcmVzc2lvbiBmb3IgYSBzaW5nbGUgdGFyZ2V0DQogIGE8LWdncGxvdChwRGF0YSh0YXJnZXRfRGF0YSksDQogICAgICAgYWVzKHggPSBBTk4yLCBmaWxsID0gQU5OMiwNCiAgICAgICAgICAgeSA9IGFzLm51bWVyaWMoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVtteV9nb2ksIF0sIGVsdCA9ICJxX25vcm0iKSkpKSArDQogIGdlb21fdmlvbGluKCkgKw0KICBnZW9tX2ppdHRlcih3aWR0aCA9IC4yKSArDQogIGxhYnMoeSA9IHBhc3RlKG15X2dvaSwiRXhwcmVzc2lvbiIpKSArDQogIHNjYWxlX3lfY29udGludW91cyh0cmFucyA9ICJsb2cyIikgKw0KICAjZmFjZXRfd3JhcCh+QU5OMywgbnJvdz0xKSArIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KSArDQogIHRoZW1lX2J3KCkgKyBjb29yZF9mbGlwKCkNCiAgcHJpbnQoYSkNCn0NCn1lbHNlew0KICBwcmludCgiTm8gc2lnbmlmaWNhbnQgbE1NIHJlc3VsdHMgdG8gcGxvdCIpDQp9DQpgYGANCg0KIyA3LjUgSGVhdG1hcCBvZiBTaWduaWZpY2FudCBHZW5lcw0KDQpJbiBhZGRpdGlvbiB0byBnZW5lcmF0aW5nIGluZGl2aWR1YWwgZ2VuZSBib3ggcGxvdHMgb3Igdm9sY2FubyBwbG90cywgd2UNCmNhbiBhZ2FpbiBjcmVhdGUgYSBoZWF0bWFwIGZyb20gb3VyIGRhdGEuIFRoaXMgdGltZSByYXRoZXIgdGhhbg0KdXRpbGl6aW5nIENWIHRvIHNlbGVjdCBnZW5lcywgd2UgY2FuIHVzZSB0aGUgUC12YWx1ZSBvciBGRFIgdmFsdWVzIHRvDQpzZWxlY3QgZ2VuZXMuIEhlcmUsIHdlIHBsb3QgYWxsIGdlbmVzIHdpdGggYW4gRkRSIFw8IDAuMDAxLg0KDQpgYGB7ciBoZWF0bWFwX3NpZ19nZW5lcywgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MjB9DQpteV9nb2lzIDwtdW5pcXVlKHN1YnNldChsbW1fcmVzdWx0cywgYEZEUmAgPCAwLjAwMSkkR2VuZSkgICAjIDEwMCB0byBwcmV2ZW50IGxvbmcgcnVudGltZQ0KDQppZihsZW5ndGgobXlfZ29pcyk8Mikgew0KICBwcmludCgiTm8gc2lnbmlmaWNhbnQgcmVzdWx0cyB0byBzaG93IikNCiANCn1lbHNlew0KDQpwaGVhdG1hcChsb2cyKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGFbbXlfZ29pcywgXSwgZWx0ID0gInFfbm9ybSIpKSwNCiAgICAgICAgIHNjYWxlID0gInJvdyIsDQogICAgICAgICBzaG93X3Jvd25hbWVzID0gVFJVRSwgc2hvd19jb2xuYW1lcyA9IFRSVUUsDQogICAgICAgICBib3JkZXJfY29sb3IgPSBOQSwNCiAgICAgICAgIGNsdXN0ZXJpbmdfbWV0aG9kID0gImF2ZXJhZ2UiLA0KICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgICBjdXRyZWVfY29scyA9IDMsIGN1dHJlZV9yb3dzID0gMiwNCiAgICAgICAgIGJyZWFrcyA9IHNlcSgtMywgMywgMC4wNSksDQogICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygicHVycGxlMyIsICJibGFjayIsICJ5ZWxsb3cyIikpKDEyMCksDQogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGNvbG9yX2xpc3QsDQogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IHBEYXRhKHRhcmdldF9EYXRhKVssIGFubl9uYW1lc10pDQp9DQpgYGANCg0KIyA4IFBhdGh3YXkgQW5hbHlzaXMNCg0KUGF0aHdheSBhbmFseXNpcyBlbmFibGVzIGV4cGxvcmF0aW9uIG9mIGRpZmZlcmVudCBhZ2dyZWdhdGUgZ2VuZSBzZXRzDQpmb3Igb3VyIGV4cGVyaW1lbnRhbCBxdWVzdGlvbnMuIEVhY2ggaW5kaXZpZHVhbCBST0kvQU9JIHNlZ21lbnQgaXMNCnNjb3JlZCBmb3IgZXZlcnkgcGF0aHdheSBvZiBpbnRlcmVzdCwgd2hpY2ggd2UgY2FuIHRoZW4gdXNlIHRvDQppbnZlc3RpZ2F0ZSBiaW9sb2dpY2FsIGRpZmZlcmVuY2VzLiBXZSB3aWxsIHBlcmZvcm0gYW5hbG9nb3VzIGFuYWx5c2VzDQphcyB0aG9zZSBvdXRsaW5lZCBpbiB0aGUgRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gYW5kIFNwYXRpYWwNCkRlY29udm9sdXRpb24gc2VjdGlvbnMgb2YgdGhlIHJlcG9ydCBmb3IgZ2VuZSBzZXQgZW5yaWNobWVudC4NCg0KIyA4LjEgU2NvcmluZyBHZW5lIFNldHMNCg0KUGF0aHdheXMgYW5kIGdlbmUgc2V0cyB3ZXJlIGRlZmluZWQgZnJvbSB0aGUgS2VnZyBCcml0ZSBkYXRhYmFzZS4gV2UgdXNlDQphbiBSIHNvZnR3YXJlIHBhY2thZ2UgY2FsbGVkIEdlbmUgU2V0IFZhcmlhdGlvbiBBbmFseXNpcyB0byBzY29yZSBlYWNoDQpzZWdtZW50IHdpdGhpbiBvdXIgc3R1ZHkuIHNlZQ0KPGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9tc2lnZGJyL3ZpZ25ldHRlcy9tc2lnZGJyLWludHJvLmh0bWw+DQpmb3Igb3B0aW9ucyBvbiBjb2xsZWN0aW9ucy4gV2UgdXNlIHRoZSBLRUdHIGFuZCBSRUFDVE9NRS4NCg0KYGBge3IgYnVpbGRfZ2VuZXNldHN9DQojZ2MoKQ0KaF9nZW5lX3NldHMgPSByYmluZChtc2lnZGJyKHNwZWNpZXMgPSAiaHVtYW4iLCBzdWJjYXRlZ29yeSA9ICJDUDpLRUdHIiksDQogICAgICAgICAgICAgICAgICAgIG1zaWdkYnIoc3BlY2llcyA9ICJodW1hbiIsIHN1YmNhdGVnb3J5ID0gIkNQOlJFQUNUT01FIikpDQojbXNpZ2RicihzcGVjaWVzID0gImh1bWFuIiwgc3ViY2F0ZWdvcnkgPSAiQ1A6QklPQ0FSVEEiKQ0KDQptc2lnZGJyX2xpc3QgPSBzcGxpdCh4ID0gaF9nZW5lX3NldHMkZ2VuZV9zeW1ib2wsIGYgPSBoX2dlbmVfc2V0cyRnc19uYW1lKQ0KDQojIHByZXBhcmUgZGYgZm9yIGFjY3VyYXRlIG1lcmdpbmcgYmFjayBnZW5lcyBsYXRlcg0KcHdfZ2VuZV9kZjwtZGF0YS5mcmFtZShQYXRod2F5ID0gbmFtZXMobXNpZ2Ricl9saXN0KSkNCnB3X2dlbmVfZGYkZ2VuZXM8LW1zaWdkYnJfbGlzdA0KYGBgDQoNCmBgYHtyIHJ1bl9nc3ZhfQ0Kc3Nnc2VhX3Jlc3VsdHMgPC0gR1NWQTo6Z3N2YShleHByID0gYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHQgPSAibG9nX3EiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBnc2V0LmlkeC5saXN0ID0gbXNpZ2Ricl9saXN0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJzc2dzZWEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5zeiA9IDUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4LnN6ID0gNTAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkNCmdlbmVTZXRPYmogPC0NCiAgTmFub1N0cmluZ0dlb014U2V0KGFzc2F5RGF0YSA9IHNzZ3NlYV9yZXN1bHRzLA0KICAgICAgICAgICAgICAgICAgICAgcGhlbm9EYXRhID0gQW5ub3RhdGVkRGF0YUZyYW1lKHBEYXRhKHRhcmdldF9EYXRhKSksDQogICAgICAgICAgICAgICAgICAgICBwcm90b2NvbERhdGEgPSBwcm90b2NvbERhdGEodGFyZ2V0X0RhdGEpLA0KICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZVR5cGUgPSAiR2VuZVNldCIsDQogICAgICAgICAgICAgICAgICAgICBjaGVjayA9IEZBTFNFKQ0KYGBgDQoNCiMgOC4yIERpZmZlcmVudGFsIGFuYWx5c2lzIG9mIHBhdGh3YXlzDQoNCmBgYHtyIExNTV9vZl9wYXRod2F5X2FuYWxpc2lzIHJlZ2lvbn0NCiMgIyBjb252ZXJ0IHRlc3QgdmFyaWFibGVzIHRvIGZhY3RvcnMNCnBEYXRhKHRhcmdldF9EYXRhKSR0ZXN0Q2xhc3MgPC0gZmFjdG9yKHBEYXRhKHRhcmdldF9EYXRhKSRBTk4xLCB1bmlxdWUodGFyZ2V0X0RhdGEkQU5OMSkpDQpwRGF0YShnZW5lU2V0T2JqKVtbInNsaWRlIl1dPC1mYWN0b3IocERhdGEoZ2VuZVNldE9iailbWyJzbGlkZV9uYW1lIl1dKQ0KI3BEYXRhKHRhcmdldF9EYXRhKSR0ZXN0UmVnaW9uPC1mYWN0b3IocERhdGEodGFyZ2V0X0RhdGEpJEFOTjIsIHVuaXF1ZShjb3VudF9tYXQkQU5OMykpDQoNCmxtbV9zc2dzZWFfcmVzdWx0cyA8LSBjKCkNCmZvciAocmVnaW9uIGluIHVuaXF1ZShwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMykpIHsNCiAgaW5kIDwtIHBEYXRhKHRhcmdldF9EYXRhKSRBTk4zID09IHJlZ2lvbg0KICBtaXhlZE91dG1jIDwtDQogICAgbWl4ZWRNb2RlbERFKGdlbmVTZXRPYmpbLCBpbmRdLA0KICAgICAgICAgICAgICAgICBlbHQgPSAiZXhwcnMiLA0KICAgICAgICAgICAgICAgICBtb2RlbEZvcm11bGEgPSB+IHRlc3RDbGFzcyArICgxIHwgc2xpZGUpLA0KICAgICAgICAgICAgICAgICBncm91cFZhciA9ICJ0ZXN0Q2xhc3MiLA0KICAgICAgICAgICAgICAgICBuQ29yZXMgPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKSwNCiAgICAgICAgICAgICAgICAgbXVsdGlDb3JlID0gVFJVRSkNCg0KICAjIGZvcm1hdCByZXN1bHRzIGFzIGRhdGEuZnJhbWUNCiAgcl9zc2dzZWFfdGVzdCA8LSBkby5jYWxsKHJiaW5kLCBtaXhlZE91dG1jWyJsc21lYW5zIiwgXSkNCiAgc3Nnc2VhX3Rlc3RzIDwtIHJvd25hbWVzKHJfc3Nnc2VhX3Rlc3QpDQogIHJfc3Nnc2VhX3Rlc3QgPC0gYXMuZGF0YS5mcmFtZShyX3NzZ3NlYV90ZXN0KQ0KICByX3NzZ3NlYV90ZXN0JENvbnRyYXN0IDwtIHNzZ3NlYV90ZXN0cw0KICAjcl9zc2dzZWFfdGVzdCRHZW5lcyA8LSBtc2lnZGJyX2xpc3Qgc2VlbXMgdW5yZWxpYWJsZSBhcyBnc3ZhIG9taXRzIHBhdGh3YXlzIGlmIGdlbmVzIGFyZSBub3QgaW4gZGF0YS4uDQoNCiAgIyB1c2UgbGFwcGx5IGluIGNhc2UgeW91IGhhdmUgbXVsdGlwbGUgbGV2ZWxzIG9mIHlvdXIgdGVzdCBmYWN0b3IgdG8NCiAgIyBjb3JyZWN0bHkgYXNzb2NpYXRlIGdlbmUgbmFtZSB3aXRoIGl0J3Mgcm93IGluIHRoZSByZXN1bHRzIHRhYmxlDQogIHJfc3Nnc2VhX3Rlc3QkUGF0aHdheSA8LQ0KICAgIHVubGlzdChsYXBwbHkoY29sbmFtZXMobWl4ZWRPdXRtYyksDQogICAgICAgICAgICAgICAgICByZXAsIG5yb3cobWl4ZWRPdXRtY1sibHNtZWFucyIsIF1bWzFdXSkpKQ0KDQogICNyX3NzZ3NlYV90ZXN0JFN1YnNldCA8LSBzdGF0dXMNCiAgcl9zc2dzZWFfdGVzdCRGRFIgPC0gcC5hZGp1c3Qocl9zc2dzZWFfdGVzdCRgUHIoPnx0fClgLCBtZXRob2QgPSAiZmRyIikNCiAgcl9zc2dzZWFfdGVzdCRTdWJzZXQgPC0gcmVnaW9uDQogIHJfc3Nnc2VhX3Rlc3QgPC0gcl9zc2dzZWFfdGVzdFssIGMoIlBhdGh3YXkiLCAiU3Vic2V0IiwgIkNvbnRyYXN0IiwgIkVzdGltYXRlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUHIoPnx0fCkiLCAiRkRSIildDQogIGxtbV9zc2dzZWFfcmVzdWx0cyA8LSByYmluZChsbW1fc3Nnc2VhX3Jlc3VsdHMsIHJfc3Nnc2VhX3Rlc3QpDQoNCiAgfQ0KICBsbW1fc3Nnc2VhX3Jlc3VsdHMgPC0gbWVyZ2UobG1tX3NzZ3NlYV9yZXN1bHRzLCBwd19nZW5lX2RmKQ0KYGBgDQoNCiMgOC4yLjEgVGFibGUgb2YgRGlmZmVyZW50YWwgYW5hbHlzaXMgb2YgcGF0aHdheXMNCg0KYGBge3IgdGFibGVfb2ZfTE1NX3BhdGh3YXlfcmVzdWx0cyByZWdpb24sIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTIwfQ0KZGF0YXRhYmxlKHN1YnNldChsbW1fc3Nnc2VhX3Jlc3VsdHMpLCByb3duYW1lcyA9IEZBTFNFLA0KICAgICAgICAgIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsIG9wdGlvbnMgPSBsaXN0ICgNCiAgICAgICAgICAgICBwYWdlTGVuZ3RoID0gMTAsDQogICAgICAgICAgICAgIGRvbSA9ICdCZnRyaXAnLA0KICAgICAgICAgICAgICBidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykNCiAgICAgICAgICAgICksDQogICAgICAgICAgY2FwdGlvbiA9ICJERSByZXN1bHRzIGZvciBQYXRod2F5cyIsZmlsdGVyPSd0b3AnKSAlPiUgZm9ybWF0Um91bmQoY29sdW1ucz1jKCJFc3RpbWF0ZSIsIlByKD58dHwpIiwiRkRSIiksIGRpZ2l0cz01KQ0KYGBgDQoNCiMgOC4zIFZ1bGNhbm9wbG90IG9mIExNTV9QYXRod2F5cw0KDQpgYGB7ciBsbW1fcHdfdnVsY2FubywgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTB9DQojIENhdGVnb3JpemUgUmVzdWx0cyBiYXNlZCBvbiBQLXZhbHVlICYgRkRSIGZvciBwbG90dGluZw0KZmNfdGhyZXNob2xkID0gMC4zDQoNCmxtbV9zc2dzZWFfcmVzdWx0cyRDb2xvciA8LSAiTlMgb3IgRkMgPCAwLjMiDQpsbW1fc3Nnc2VhX3Jlc3VsdHMkQ29sb3JbbG1tX3NzZ3NlYV9yZXN1bHRzJGBQcig+fHR8KWAgPCAwLjA1XSA8LSAiUCA8IDAuMDUiDQpsbW1fc3Nnc2VhX3Jlc3VsdHMkQ29sb3JbbG1tX3NzZ3NlYV9yZXN1bHRzJEZEUiA8IDAuMDVdIDwtICJGRFIgPCAwLjA1Ig0KbG1tX3NzZ3NlYV9yZXN1bHRzJENvbG9yW2xtbV9zc2dzZWFfcmVzdWx0cyRGRFIgPCAwLjAwMV0gPC0gIkZEUiA8IDAuMDAxIg0KbG1tX3NzZ3NlYV9yZXN1bHRzJENvbG9yW2FicyhsbW1fc3Nnc2VhX3Jlc3VsdHMkRXN0aW1hdGUpIDwgZmNfdGhyZXNob2xkXSA8LSAiTlMgb3IgRkMgPCAwLjMiDQpsbW1fc3Nnc2VhX3Jlc3VsdHMkQ29sb3IgPC0gZmFjdG9yKGxtbV9zc2dzZWFfcmVzdWx0cyRDb2xvciwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk5TIG9yIEZDIDwgMC4zIiwgIlAgPCAwLjA1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZEUiA8IDAuMDUiLCAiRkRSIDwgMC4wMDEiKSkNCg0KIyBwaWNrIHRvcCBwdyBmb3IgZWl0aGVyIHNpZGUgb2Ygdm9sY2FubyB0byBsYWJlbA0KIyBvcmRlciBwdyBmb3IgY29udmVuaWVuY2U6DQpsbW1fc3Nnc2VhX3Jlc3VsdHMkaW52ZXJ0X1AgPC0gKC1sb2cxMChsbW1fc3Nnc2VhX3Jlc3VsdHMkYFByKD58dHwpYCkpICogc2lnbihsbW1fc3Nnc2VhX3Jlc3VsdHMkRXN0aW1hdGUpDQp0b3Bfc3Nnc2VhX2cgPC0gYygpDQoNCiNsb29wIGhlcmUgb3ZlciB0ZXN0ZWQgY29uZGl0aW9ucyBpZiBhcHBsaWNhYmxlDQoNCmZvcihjIGluIHVuaXF1ZShsbW1fc3Nnc2VhX3Jlc3VsdHMkU3Vic2V0KSApIHsNCiAgbG1tX3Jlc3VsdHNfc2xpY2UgPSBsbW1fc3Nnc2VhX3Jlc3VsdHNbbG1tX3NzZ3NlYV9yZXN1bHRzJFN1YnNldCA9PSBjLF0NCiB0b3Bfc3Nnc2VhX2cgPC0gYyh0b3Bfc3Nnc2VhX2csDQogICAgICAgICBsbW1fc3Nnc2VhX3Jlc3VsdHNbaW5kLCAnUGF0aHdheSddWw0KICAgICAgICAgICAgICAgb3JkZXIobG1tX3NzZ3NlYV9yZXN1bHRzW2luZCwgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjE1XV0sDQogICAgICAgICBsbW1fc3Nnc2VhX3Jlc3VsdHNbaW5kLCAnUGF0aHdheSddWw0KICAgICAgICAgICAgICAgb3JkZXIobG1tX3NzZ3NlYV9yZXN1bHRzW2luZCwgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMToxNV1dKQ0KfQ0KIA0KdG9wX3NzZ3NlYV9nIDwtIHVuaXF1ZSh0b3Bfc3Nnc2VhX2cpDQpsbW1fc3Nnc2VhX3Jlc3VsdHMgPC0gbG1tX3NzZ3NlYV9yZXN1bHRzWywgLTEqbmNvbChsbW1fc3Nnc2VhX3Jlc3VsdHMpXSAjIHJlbW92ZSBpbnZlcnRfUCBmcm9tIG1hdHJpeA0KDQojbG1tX3NzZ3NlYV9yZXN1bHRzX3NsaWNlIDwtIGxtbV9zc2dzZWFfcmVzdWx0c19zbGljZVtsbW1fc3Nnc2VhX3Jlc3VsdHNfc2xpY2UkRkRSIDwgMSxdDQoNCiMgR3JhcGggcmVzdWx0cw0KZ2dwbG90KGxtbV9zc2dzZWFfcmVzdWx0cywNCiAgICAgICBhZXMoeCA9IEVzdGltYXRlLCB5ID0gLWxvZzEwKGBQcig+fHR8KWApLA0KICAgICAgICAgICBjb2xvciA9IENvbG9yLCBsYWJlbCA9IFBhdGh3YXkpKSArDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygwLjIsIC0wLjIpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIGxhYnMoeCA9IHBhc3RlKGxtbV9yZXN1bHRzX3NsaWNlJENvbnRyYXN0LCAibG9nMihGQykiKSwNCiAgICAgICAgIHkgPSAiU2lnbmlmaWNhbmNlLCAtbG9nMTAoUCkiLA0KICAgICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbmNlIikgKw0KICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKGBGRFIgPCAwLjAwMWAgPSAiZG9kZ2VyYmx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYEZEUiA8IDAuMDVgID0gImxpZ2h0Ymx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYFAgPCAwLjA1YCA9ICJvcmFuZ2UyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgTlMgb3IgRkMgPCAwLjVgID0gImdyYXkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gNCkpKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMDUpKSkgKw0KICAgIGdlb21fdGV4dF9yZXBlbChkYXRhID0gc3Vic2V0KGxtbV9zc2dzZWFfcmVzdWx0cywgQ29sb3IgPT0gIkZEUiA8IDAuMDUiIHwgQ29sb3IgPT0gIkZEUiA8IDAuMDAxIiksDQogICAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IDAuMTUsIGNvbG9yID0gImJsYWNrIixzaXplPTMsDQogICAgICAgICAgICAgICAgICAgbWluLnNlZ21lbnQubGVuZ3RoID0gLjEsIGJveC5wYWRkaW5nID0gLjIsIGx3ZCA9IDIsDQogICAgICAgICAgICAgICAgICAgbWF4Lm92ZXJsYXBzID0gNTApICsNCiAgICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNikgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogICAgZmFjZXRfd3JhcCh+U3Vic2V0LCBzY2FsZXMgPSAiZnJlZV95IikNCg0KIyAgICArZmFjZXRfd3JhcCh+U3Vic2V0LCBzY2FsZXMgPSAiZnJlZV95IikpDQpgYGANCg0KIyA4LjQgaGVhdG1hcCBvZiBwYXRod2F5cw0KDQpgYGB7ciBwd19oZWF0bWFwLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0yMH0NCiAgYWN0aXZlX3B3PC1maWx0ZXIobG1tX3NzZ3NlYV9yZXN1bHRzLCBgUHIoPnx0fClgIDwgMC4wMDEpJFBhdGh3YXkNCiAgYWN0aXZlX3B3PC1maWx0ZXIobG1tX3NzZ3NlYV9yZXN1bHRzLCBGRFIgPCAwLjAwMSApJFBhdGh3YXkNCiAgI2FjdGl2ZV9wdzwtZmlsdGVyKGxtbV9zc2dzZWFfcmVzdWx0cywgQ29sb3IgPT0gIkZEUiA8IDAuMDAxIikkUGF0aHdheQ0KICANCiAgDQogIGFjdGl2ZV9wdzwtdG9wX3NzZ3NlYV9nDQogIA0KICBpZiAobGVuZ3RoKGFjdGl2ZV9wdyk+MSkgew0KICANCiAgICBwcmludCgiZ28iKQ0KICAgIA0KICBwd19tYXRyaXg8LWFzc2F5RGF0YUVsZW1lbnQoZ2VuZVNldE9iaiwgZWx0ID0gImV4cHJzIikNCg0KICBwaGVhdG1hcChwd19tYXRyaXhbYWN0aXZlX3B3LF0sDQogICAgICAgICBzY2FsZSA9ICJyb3ciLA0KICAgICAgICAgc2hvd19yb3duYW1lcyA9IFRSVUUsIHNob3dfY29sbmFtZXMgPSBUUlVFLA0KICAgICAgICAgZm9udHNpemVfcm93ID0gMTAsDQogICAgICAgICBib3JkZXJfY29sb3IgPSBOQSwNCiAgICAgICAgIGNsdXN0ZXJpbmdfbWV0aG9kID0gImF2ZXJhZ2UiLA0KICAgICAgICAgI2NsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiZXVjbGlkZWFuIiwNCiAgICAgICAgIGN1dHJlZV9jb2xzID0gMiwgY3V0cmVlX3Jvd3MgPSAyLA0KICAgICAgICAgYnJlYWtzID0gc2VxKC0zLCAzLCAwLjA1KSwNCiAgICAgICAgICNjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygicHVycGxlMyIsICJibGFjayIsICJ5ZWxsb3cyIikpKDEyMCksDQogICAgICAgICBtYWluID0gIkhlYXRtYXAgb2Ygc2VsZWN0ZWQgUGF0aHdheXMiLA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBjb2xvcl9saXN0LA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBwRGF0YSh0YXJnZXRfRGF0YSlbLCBhbm5fbmFtZXNdKQ0KICANCiAgfWVsc2V7DQogICAgcHJpbnQoIk5vIHNpZ25pZmljYW50IHJlc3VsdHMgdG8gZGlzcGxheSIpDQogIH0NCmBgYA0KDQojIDguNSBmZ3NlYSBwYXRod2F5IGFuYWx5c2lzDQoNCkFub3RoZXIgb3B0aW9uIGZvciBwYXRod2F5IGFuYWx5c2lzIGlzIHRoZSBSIHNvZnR3YXJlIHBhY2thZ2UgY2FsbGVkIEZhc3QgR2VuZSBTZXQgRW5yaWNobWVudCBBbmFseXNpcy4gSXQgaXMgYSBwcmUtcmFua2VkIG1ldGhvZCB0aGF0IHVzZXMgdGhlIExNTSByZXN1bHRzIGNvbWJpbmVkIHdpdGggdGhlIHNhbWUgcGF0aHdheSBsaXN0IGZyb20gbXNpZ2RiciBhcyBHU1ZBLg0KDQpgYGB7ciBmZ3NlYX0NCmZnc2VhX3Jlc3VsdHNfYWxsIDwtIGMoKQ0KZm9yIChjb250cmFzdCBpbiB1bmlxdWUobG1tX3Jlc3VsdHMkQ29udHJhc3QpKSB7DQogICAgDQogICAgcmFua3MgPC0gbG1tX3Jlc3VsdHMkRXN0aW1hdGVbbG1tX3Jlc3VsdHMkQ29udHJhc3QgPT0gY29udHJhc3RdDQogICAgbmFtZXMocmFua3MpIDwtIGxtbV9yZXN1bHRzJEdlbmVbbG1tX3Jlc3VsdHMkQ29udHJhc3QgPT0gY29udHJhc3RdDQogICAgDQogICAgZmdzZWFfcmVzdWx0cyA8LSBmZ3NlYShtc2lnZGJyX2xpc3QsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmtzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBlcHMgPSAwLjAsDQogICAgICAgICAgICAgICAgICAgICAgICAgbWluU2l6ZT0xNSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgbWF4U2l6ZSA9IDUwMCkNCiAgICBmZ3NlYV9yZXN1bHRzJENvbnRyYXN0IDwtIGNvbnRyYXN0DQogICAgZmdzZWFfcmVzdWx0c19hbGwgPC0gcmJpbmQoZmdzZWFfcmVzdWx0c19hbGwsIGZnc2VhX3Jlc3VsdHMpDQogICAgDQp9DQoNCmZnc2VhX3JlYWN0b21lIDwtIGZnc2VhX3Jlc3VsdHNfYWxsW2dyZXAoIlJFQUNUT01FIiwgcGF0aHdheSksXQ0KZmdzZWFfcmVhY3RvbWUkcGF0aHdheSA8LSBnc3ViKCJSRUFDVE9NRV8iLCAiIiwgZmdzZWFfcmVhY3RvbWUkcGF0aHdheSkNCmZnc2VhX3JlYWN0b21lJHBhdGh3YXkgPC0gZ3N1YigiXyIsICIgIiwgZmdzZWFfcmVhY3RvbWUkcGF0aHdheSkNCmBgYA0KDQpgYGB7ciBmZ3NlYV9wd192dWxjYW5vLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMH0NCiMgQ2F0ZWdvcml6ZSBSZXN1bHRzIGJhc2VkIG9uIFAtdmFsdWUgJiBGRFIgZm9yIHBsb3R0aW5nDQpmY190aHJlc2hvbGQgPSAwLjMNCg0KDQpmZ3NlYV9yZXN1bHRzX2FsbCRDb2xvciA8LSAiTlMgb3IgRkMgPCAwLjMiDQpmZ3NlYV9yZXN1bHRzX2FsbCRDb2xvcltmZ3NlYV9yZXN1bHRzX2FsbCRwdmFsIDwgMC4wNV0gPC0gIlAgPCAwLjA1Ig0KZmdzZWFfcmVzdWx0c19hbGwkQ29sb3JbZmdzZWFfcmVzdWx0c19hbGwkcGFkaiA8IDAuMDVdIDwtICJGRFIgPCAwLjA1Ig0KZmdzZWFfcmVzdWx0c19hbGwkQ29sb3JbZmdzZWFfcmVzdWx0c19hbGwkcGFkaiA8IDAuMDAxXSA8LSAiRkRSIDwgMC4wMDEiDQpmZ3NlYV9yZXN1bHRzX2FsbCRDb2xvclthYnMoZmdzZWFfcmVzdWx0c19hbGwkRVMpIDwgZmNfdGhyZXNob2xkXSA8LSAiTlMgb3IgRkMgPCAwLjMiDQpmZ3NlYV9yZXN1bHRzX2FsbCRDb2xvciA8LSBmYWN0b3IoZmdzZWFfcmVzdWx0c19hbGwkQ29sb3IsDQogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJOUyBvciBGQyA8IDAuMyIsICJQIDwgMC4wNSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGRFIgPCAwLjA1IiwgIkZEUiA8IDAuMDAxIikpDQoNCnBsdCA8LSBodG1sdG9vbHM6OnRhZ0xpc3QoKQ0KY291bnRlciA9IDENCmZvcihjIGluIHVuaXF1ZShmZ3NlYV9yZXN1bHRzX2FsbCRDb250cmFzdCkgKSB7DQogIGZnc2VhX3Jlc3VsdHNfc2xpY2UgPSBmZ3NlYV9yZXN1bHRzX2FsbFtmZ3NlYV9yZXN1bHRzX2FsbCRDb250cmFzdCA9PSBjLF0NCg0KICAjICMgcGljayB0b3AgZ2VuZXMgZm9yIGVpdGhlciBzaWRlIG9mIHZvbGNhbm8gdG8gbGFiZWwNCiAgIyAjIG9yZGVyIGdlbmVzIGZvciBjb252ZW5pZW5jZToNCiAgZmdzZWFfcmVzdWx0c19zbGljZSRpbnZlcnRfUCA8LSAoLWxvZzEwKGZnc2VhX3Jlc3VsdHNfc2xpY2UkcHZhbCkpICogc2lnbihmZ3NlYV9yZXN1bHRzX3NsaWNlJEVTKQ0KICANCiAgIyAjbG9vcCBoZXJlIG92ZXIgdGVzdGVkIGNvbmRpdGlvbnMgaWYgYXBwbGljYWJsZQ0KICAjdG9wX2Znc2VhX2cgPC0gYygpDQogIHRvcF9mZ3NlYV9nIDwtIGMoZmdzZWFfcmVzdWx0c19zbGljZVssICdwYXRod2F5J11bDQogICAgICAgICAgICAgICAgICAgICAgICBvcmRlcihmZ3NlYV9yZXN1bHRzX3NsaWNlWywgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjE1XV0sDQogICAgICAgICAgICAgICAgICAgIGZnc2VhX3Jlc3VsdHNfc2xpY2VbLCAncGF0aHdheSddWw0KICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIoZmdzZWFfcmVzdWx0c19zbGljZVssICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gRkFMU0UpWzE6MTVdXSkNCiAgIA0KICAjIGZvciAoY2VsbHR5cGUgaW4gdW5pcXVlKGxtbV9yZXN1bHRzJFN1YnNldCkpIHsNCiAgIyAgICAgIHRvcF9mZ3NlYV9nIDwtIGModG9wX2Znc2VhX2csDQogICMgICAgICAgICAgICAgICAgICBmZ3NlYV9yZXN1bHRzX3NsaWNlW2Znc2VhX3Jlc3VsdHNfc2xpY2UkU3Vic2V0PT1jZWxsdHlwZSwgJ3BhdGh3YXknXVsNCiAgIyAgICAgICAgICAgICAgICAgICAgICBvcmRlcihmZ3NlYV9yZXN1bHRzX3NsaWNlWywgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjIwXV0sDQogICMgICAgICAgICAgICAgICAgICBmZ3NlYV9yZXN1bHRzX3NsaWNlW2Znc2VhX3Jlc3VsdHNfc2xpY2UkU3Vic2V0PT1jZWxsdHlwZSwgJ3BhdGh3YXknXVsNCiAgIyAgICAgICAgICAgICAgICAgICAgICBvcmRlcihmZ3NlYV9yZXN1bHRzX3NsaWNlWywgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMToyMF1dKQ0KICAjIH0NCiAgDQogIHRvcF9mZ3NlYV9nIDwtIHVuaXF1ZSh0b3BfZmdzZWFfZykNCiAgI2Znc2VhX3Jlc3VsdHNfc2xpY2UgPC0gZmdzZWFfcmVzdWx0c19zbGljZVssIC0xKm5jb2woZmdzZWFfcmVzdWx0c19zbGljZSldICMgcmVtb3ZlIGludmVydF9QIGZyb20gbWF0cml4DQogIA0KICAjIEdyYXBoIHJlc3VsdHMNCiAgZHlucGxvdCA8LSBnZ3Bsb3QoZmdzZWFfcmVzdWx0c19zbGljZSwNCiAgICAgICAgIGFlcyh4ID0gTkVTLCB5ID0gLWxvZzEwKHB2YWwpLA0KICAgICAgICAgICAgIGNvbG9yID0gQ29sb3IsIGxhYmVsID0gcGF0aHdheSkpICsNCiAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoZmNfdGhyZXNob2xkLC1mY190aHJlc2hvbGQpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgZ2VvbV9wb2ludCgpICsNCiAgICAgIGxhYnMoeCA9IHBhc3RlKGMsICIgRkMiKSwNCiAgICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICAgIGNvbG9yID0gIlNpZ25pZmljYW5jZSIpICsNCiAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKGBGRFIgPCAwLjAwMWAgPSAiZG9kZ2VyYmx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBQIDwgMC4wNWAgPSAib3JhbmdlMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgTlMgb3IgRkMgPCAwLjNgID0gImdyYXkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICAgIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwwLjA1KSkpICsNCiAgICAgIGdlb21fdGV4dF9yZXBlbChkYXRhID0gc3Vic2V0KGZnc2VhX3Jlc3VsdHNfc2xpY2UsIENvbG9yID09ICJQIDwgMC4wNSIgfCBDb2xvciA9PSAiRkRSIDwgMC4wNSIgfCBDb2xvciA9PSAiRkRSIDwgMC4wMDEiKSwNCiAgICAgICAgICAgICAgICAgICAgIHBvaW50LnBhZGRpbmcgPSAwLjE1LCBjb2xvciA9ICJibGFjayIsc2l6ZT01LA0KICAgICAgICAgICAgICAgICAgICAgbWluLnNlZ21lbnQubGVuZ3RoID0gLjEsIGJveC5wYWRkaW5nID0gLjIsIGx3ZCA9IDIsDQogICAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSA1MCkgKw0KICAgICAgdGhlbWVfYncoYmFzZV9zaXplID0gMTYpICsNCiAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSAjKw0KICAgICAgI2ZhY2V0X3dyYXAoflN1YnNldCwgc2NhbGVzID0gImZyZWVfeSIpDQogICANCiAgcGx0W1tjb3VudGVyXV0gPC0gYXNfd2lkZ2V0KGdncGxvdGx5KGR5bnBsb3QpKQ0KICBjb3VudGVyIDwtIGNvdW50ZXIgKyAxDQogICNkYXRhdGFibGUoc3Vic2V0KHJlc3VsdHMsIEdlbmUgJWluJSBHT0kpLCByb3duYW1lcz1GQUxTRSxjYXB0aW9uID0gcGFzdGUoIkRFIHJlc3VsdHMgIiwgYWN0aXZlX2dyb3VwMSwiIHZzICIsIGFjdGl2ZV9ncm91cDIpKQ0KfQ0KcGx0DQpgYGANCg0KIyA4LjYgVmlzdWFsaXplIEtFR0cgcGF0aHdheQ0KDQpHZXQgcGF0aHdheSB2aXN1YWxpemF0aW9uIGZyb20gS0VHRy4gY2x1c3RlclByb2ZpbGVyIGdyYWJzIHRoZSBLRUdHIHBhdGh3YXkgSURzIGFuZCBjb21iaW5lZCB3aXRoIHBhdGh2aWV3IGl0IGNhbiBkb3dubG9hZCBhIHBhdGh3YXkgaW1hZ2UgZnJvbSB0aGUgS0VHRyBkYXRhYmFzZS4NClRoZSBMTU0gcmVzdWx0cyBhcmUgZ2l2ZW4gd2l0aCB0aGUgcGF0aHdheSBpZCB0byB2aXN1YWxpemUgdXAgYW5kIGRvd24gcmVndWxhdGVkIGdlbmVzLg0KDQpgYGB7cn0NCmdldF93cF9nbXRmaWxlIDwtIGZ1bmN0aW9uKCkgew0KICAgIHdwdXJsIDwtICdodHRwczovL3dpa2lwYXRod2F5cy1kYXRhLndtY2xvdWQub3JnL2N1cnJlbnQvZ210LycNCiAgICB4IDwtIHJlYWRMaW5lcyh3cHVybCkNCiAgICB5IDwtIHhbZ3JlcCgnXFwuZ210Jyx4KV0NCiAgICBzdWIoIi4qKHdpa2lwYXRod2F5cy0uKlxcLmdtdCkuKiIsICJcXDEiLCAgeVtncmVwKCdGaWxlJywgeSldKQ0KfQ0KDQpnZXRfd3BfZGF0YSA8LSBmdW5jdGlvbihvcmdhbmlzbSkgew0KICAgIG9yZ2FuaXNtIDwtIHN1YigiICIsICJfIiwgb3JnYW5pc20pDQogICAgZ210ZmlsZSA8LSBnZXRfd3BfZ210ZmlsZSgpDQogICAgd3B1cmwgPC0gJ2h0dHBzOi8vd2lraXBhdGh3YXlzLWRhdGEud21jbG91ZC5vcmcvY3VycmVudC9nbXQvJw0KICAgIHVybCA8LSBwYXN0ZTAod3B1cmwsDQogICAgICAgICAgICAgICAgICBnbXRmaWxlW2dyZXAob3JnYW5pc20sIGdtdGZpbGUpXSkNCiAgICBmIDwtIHRlbXBmaWxlKGZpbGVleHQgPSAiLmdtdCIpDQogICAgZGwgPC0gbXlkb3dubG9hZCh1cmwsIGRlc3RmaWxlID0gZikNCiAgICBpZiAoaXMubnVsbChmKSkgew0KICAgICAgICBtZXNzYWdlKCJmYWlsIHRvIGRvd25sb2FkIHdpa2lQYXRod2F5cyBkYXRhLi4uIikNCiAgICAgICAgcmV0dXJuKE5VTEwpDQogICAgfQ0KICAgIHJlYWQuZ210LndwKGYpDQp9DQpgYGANCg0KDQpgYGB7ciBnZXQgb25saW5lIGF2YWlsYWJsZSBwYXRod2F5cywgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTB9DQojIGVucmljaEtFR0cgZ3JhYiBvbmxpbmUgcGF0aHdheXMNCmdlbmUgPC0gIHRhcmdldF9EYXRhQGZlYXR1cmVEYXRhQGRhdGFbWyJHZW5lSUQiXV0NCmtrIDwtIGVucmljaEtFR0coZ2VuZSAgICAgICAgID0gZ2VuZSwNCiAgICAgICAgICAgICAgICAgb3JnYW5pc20gICAgID0gJ2hzYScsDQogICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMSkNCg0KZW5yaWNoS0VHR19yZXN1bHRzIDwtIGtrQHJlc3VsdA0KDQojIE1hdGNoIGNvbHVtbiB2YWx1ZXMgZm9yIGxpbmsNCiMgZnNnZWEgcnVuDQpmZ3NlYV9yZXN1bHRzJERlc2NyaXB0aW9uIDwtIGdzdWIoIktFR0dfIiwgIiIsIGZnc2VhX3Jlc3VsdHMkcGF0aHdheSkNCmZnc2VhX3Jlc3VsdHMkRGVzY3JpcHRpb24gPC0gZ3N1YigiXyIsICIgIiwgZmdzZWFfcmVzdWx0cyREZXNjcmlwdGlvbikNCiMgc3NnZWEgcnVuDQojIGxtbV9zc2dzZWFfcmVzdWx0c19kJERlc2NyaXB0aW9uIDwtIGdzdWIoIktFR0dfIiwgIiIsIGxtbV9zc2dzZWFfcmVzdWx0c19kJFBhdGh3YXkpDQojIGxtbV9zc2dzZWFfcmVzdWx0c19kJERlc2NyaXB0aW9uIDwtIGdzdWIoIl8iLCAiICIsIGxtbV9zc2dzZWFfcmVzdWx0c19kJERlc2NyaXB0aW9uKQ0KDQplbnJpY2hLRUdHX3Jlc3VsdHMkRGVzY3JpcHRpb24gPC0gdG91cHBlcihlbnJpY2hLRUdHX3Jlc3VsdHMkRGVzY3JpcHRpb24pDQoNCiMgQ3JlYXRlIGRmIGZvciBtYXRjaGluZyBLRUdHIHJlc3VsdHMNCktFR0dJRHMgPC0gZmdzZWFfcmVzdWx0cyNbZmdzZWFfcmVzdWx0cyRDb250cmFzdCA9PSAiUHJvX1R1YiAtIERpc19UdWIiXQ0KS0VHR0lEcyA8LSBLRUdHSURzICU+JSBpbm5lcl9qb2luKGVucmljaEtFR0dfcmVzdWx0cywgYnkgPSAnRGVzY3JpcHRpb24nKSAlPiUgc2VsZWN0KERlc2NyaXB0aW9uLCBJRCwgZ2VuZUlELCBsZWFkaW5nRWRnZSwgcGFkaikNCg0KIyBDcmVhdGUgZ2VuZUxpc3Qgd2l0aCBERSByZXN1bHRzIGZvciB2aXN1YWxpemF0aW9uIHBhdGh3YXkgZ2VuZXMgDQpnZW5lbGlzdCA8LSBsbW1fcmVzdWx0cyRFc3RpbWF0ZQ0KbmFtZXMoZ2VuZWxpc3QpIDwtIHRhcmdldF9EYXRhQGZlYXR1cmVEYXRhQGRhdGFbWyJHZW5lSUQiXV0NCg0KIyBDaG9vc2UgbWFudWFsIG9yIGhpZ2hlc3Qgc2lnbmlmaWNhbnQgcGF0aHdheQ0KcGF0aHdheV9uYW1lIDwtICJJTlNVTElOIFNJR05BTElORyBQQVRIV0FZIiAjIG1hbnVhbCBwYXRod2F5IHF1ZXJ5DQpwYXRod2F5X25hbWUgPC0gdG91cHBlcihwYXRod2F5X25hbWUpDQppZiAocGF0aHdheV9uYW1lID09ICIiKSB7DQogIHBhdGh3YXlfbmFtZSA8LSBLRUdHSURzW29yZGVyKEtFR0dJRHMkcGFkaiwgZGVjcmVhc2luZyA9IEZBTFNFKSwgXVsxXSREZXNjcmlwdGlvbg0KfQ0KcGF0aHdheWlkIDwtIEtFR0dJRHMkSURbS0VHR0lEcyREZXNjcmlwdGlvbiA9PSBwYXRod2F5X25hbWVdDQoNCiMgR3JhYiBwYXRod2F5IGltYWdlIHdpdGggZ2VuZSBpbmZvLCBzYXZlIGFuZCBwbG90DQp2aWV3UGF0aCA8LSBwYXRodmlldyhnZW5lLmRhdGEgID0gZ2VuZWxpc3QsDQogICAgICAgICAgICAgICAgICAgICBwYXRod2F5LmlkID0gcGF0aHdheWlkLA0KICAgICAgICAgICAgICAgICAgICAgc3BlY2llcyAgICA9ICJoc2EiLA0KICAgICAgICAgICAgICAgICAgICAgb3V0LnN1ZmZpeCA9ICJnZW5lc2luZm8iLCBrZWdnLm5hdGl2ZSA9IFQsIHNhbWUubGF5ZXIgPSBGKQ0KaW1nIDwtIHJlYWRQTkcocGFzdGUocGF0aHdheWlkLCAiLmdlbmVzaW5mby5wbmciLCBzZXAgPSAiIikpDQpncmlkOjpncmlkLnJhc3RlcihpbWcpDQpgYGANCg0KIyMgQmFycGxvdCBwYXRod2F5cw0KDQpDbGVhciB2aXp1YWxpemF0aW9uIG9mIHRoZSB0b3AgMTUgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIGV4cHJlc3NlZCBSZWFjdG9tZSBwYXRod2F5cy4gUGF0aHdheXMgd2l0aCBhIGFkanVzdGVkIHAtdmFsdWUgb2YgMC4wNSBvciBoaWdoZXIgd2lsbCBiZSBwcmVzZW50ZWQgYXMgcmVkIA0KYW5kIGJlbG93IDAuMDUgYXMgZ3JlZW4uDQoNCmBgYHtyIGhvcml6b250YWwgcGF0aHdheSBiYXIgcGxvdCwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MjB9DQp0b3BfYWxsIDwtIGMoKQ0KZm9yIChjb250cmFzdCBpbiB1bmlxdWUoZmdzZWFfcmVhY3RvbWUkQ29udHJhc3QpKSB7DQogICAgIyBhY3RpdmVfZ3JvdXAxIDwtIGNvbnRyYXN0X2xpc3RbY29udHJhc3RdW1sxXV1bWzFdXQ0KICAgICMgYWN0aXZlX2dyb3VwMiA8LSBjb250cmFzdF9saXN0W2NvbnRyYXN0XVtbMV1dW1syXV0NCiAgICAjIGluZCA8LSBwRGF0YSh0YXJnZXRfRGF0YSlbcERhdGEodGFyZ2V0X0RhdGEpJEFOTjIgPT0gYWN0aXZlX2dyb3VwMSB8IHBEYXRhKHRhcmdldF9EYXRhKSRBTk4yID09IGFjdGl2ZV9ncm91cDJdDQogICAgdG9wIDwtIGZnc2VhX3JlYWN0b21lW2Znc2VhX3JlYWN0b21lJENvbnRyYXN0ID09IGNvbnRyYXN0XQ0KICAgIHRvcHBvcyA8LSB0b3Bbb3JkZXIodG9wJE5FUywgZGVjcmVhc2luZyA9IFRSVUUpXVswOjE1XQ0KICAgIHRvcG5lZyA8LSB0b3Bbb3JkZXIodG9wJE5FUywgZGVjcmVhc2luZyA9IEZBTFNFKV1bMDoxNV0NCiAgICB0b3AgPC0gcmJpbmQodG9wcG9zLCB0b3BuZWcpDQogICAgDQogICAgdG9wJENvbG9yW3RvcCRwYWRqIDwgMC4wNV0gPC0gInBhZGogPCAwLjA1Ig0KICAgIHRvcCRDb2xvclt0b3AkcGFkaiA9PSAwLjA1IHwgdG9wJHBhZGogPiAwLjA1XSA8LSAicGFkaiA+IDAuMDUiDQogICAgdG9wX2FsbCA8LSByYmluZCh0b3BfYWxsLCB0b3ApDQogICAgYmFycGxvdCA8LSBnZ3Bsb3QodG9wLGFlcyh4PXJlb3JkZXIocGF0aHdheSwgTkVTKSx5PU5FUyxmaWxsPUNvbG9yKSkgKyANCiAgICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyX3dyYXAoeCwgd2lkdGggPSAxMDApKSArIA0KICAgICAgZ2VvbV9jb2wocG9zaXRpb249ImRvZGdlIikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjMkVDQzcxIiwgIiNFNzRDM0MiKSkgKw0KICAgICAgZ2d0aXRsZShwYXN0ZSgiVG9wIDE1ICsgYW5kIC0gTkVTIHJlYWN0b21lIHBhdGh3YXlzIGZyb20iLCBjb250cmFzdCkpICsgDQogICAgICB4bGFiKCJwYXRod2F5IikgKyB5bGFiKCJOb3JtYWxpemVkIEVucmljaG1lbnQgU2NvcmUiKSArDQogICAgICBjb29yZF9mbGlwKCkgKyANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKw0KICAgICAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkNCiAgICAgICNmYWNldF93cmFwKH5Db250cmFzdCwgc2NhbGVzID0gImZyZWVfeSIpDQogICAgcHJpbnQoYmFycGxvdCkNCn0NCmBgYA0KDQojIDkgU3BhdGlhbCBEZWNvbnZvbHV0aW9uDQoNCiMjIDkuMSBDYWxjdWxhdGUgYmFja2dyb3VuZHMNCg0KYGBge3Igc3BhdGlhbF9kZWNvbl9iZ30NCiNnYygpDQpiZyA9IGRlcml2ZV9HZW9NeF9iYWNrZ3JvdW5kKA0KICBub3JtID0gYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSAsIGVsdCA9ICJxX25vcm0iKSwNCiAgcHJvYmVwb29sID0gZkRhdGEodGFyZ2V0X0RhdGEpJE1vZHVsZSwNCiAgbmVnbmFtZXMgPSBjKCJOZWdQcm9iZS1DVFAwMSIsIk5lZ1Byb2JlLUtpbG8iLCJOZWdhdGl2ZSBQcm9iZSIsICJOZWdQcm9iZS1XVFgiICkpDQogICNuZWduYW1lcyA9ICJOZWdQcm9iZS1XVFgiKQ0KDQpgYGANCg0KIyMgOS4yIExvYWQgY2VsbCBwcm9maWxlDQoNCkEgImNlbGwgcHJvZmlsZSBtYXRyaXgiIGlzIGEgcHJlLWRlZmluZWQgbWF0cml4IHRoYXQgc3BlY2lmaWVzIHRoZQ0KZXhwZWN0ZWQgZXhwcmVzc2lvbiBwcm9maWxlcyBvZiBlYWNoIGNlbGwgdHlwZSBpbiB0aGUgZXhwZXJpbWVudC4gVGhlDQpTcGF0aWFsRGVjb24gbGlicmFyeSBjb21lcyB3aXRoIG9uZSBzdWNoIG1hdHJpeCBwcmUtbG9hZGVkLCB0aGUNCiJTYWZlVE1FIiBtYXRyaXgsIGRlc2lnbmVkIGZvciBlc3RpbWF0aW9uIG9mIGltbXVuZSBhbmQgc3Ryb21hIGNlbGxzIGluDQp0aGUgdHVtb3IgbWljcm9lbnZpcm9ubWVudC4gKFRoaXMgbWF0cml4IHdhcyBkZXNpZ25lZCB0byBhdm9pZCBnZW5lcw0KY29tbW9ubHkgZXhwcmVzc2VkIGJ5IGNhbmNlciBjZWxsczsgc2VlIHRoZSBTcGF0aWFsRGVjb24gbWFudXNjcmlwdCBmb3INCmRldGFpbHMuKS4gT3RoZXJ3aXNlLCBsb2FkIHNwZWNpZmljIHByb2ZpbGVzIGZyb20NCjxodHRwczovL2dpdGh1Yi5jb20vTmFub3N0cmluZy1CaW9zdGF0cy9DZWxsUHJvZmlsZUxpYnJhcnkvdHJlZS9OZXdQcm9maWxlTWF0cmljZXM+DQoNCmBgYHtyIGxvYWRfY2VsbF9wcm9maWxlc30NCiNzYWZlVE1FDQpkYXRhKCJzYWZlVE1FIikNCmRhdGEoInNhZmVUTUUubWF0Y2hlcyIpDQpjdXJyZW50X2NlbGxfcHJvZmlsZTwtc2FmZVRNRQ0KDQojc2VlOiBodHRwczovL2dpdGh1Yi5jb20vTmFub3N0cmluZy1CaW9zdGF0cy9DZWxsUHJvZmlsZUxpYnJhcnkvdHJlZS9OZXdQcm9maWxlTWF0cmljZXMNCg0KY3VycmVudF9jZWxsX3Byb2ZpbGUgPC0gZG93bmxvYWRfcHJvZmlsZV9tYXRyaXgoc3BlY2llcyA9ICJIdW1hbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFnZV9ncm91cCA9ICJBZHVsdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hdHJpeG5hbWUgPSAiS2lkbmV5X0hDQSIpDQoNCmhlYXRtYXAoc3dlZXAoY3VycmVudF9jZWxsX3Byb2ZpbGUsIDEsIGFwcGx5KGN1cnJlbnRfY2VsbF9wcm9maWxlLCAxLCBtYXgpLCAiLyIpLA0KICAgICAgICBsYWJSb3cgPSBOQSwgbWFyZ2lucyA9IGMoMTAsIDUpLCBjZXhDb2wgPSAwLjcpDQpgYGANCg0KIyA5LjMgUnVuIHNwYXRpYWwgZGVjb252b2x1dGlvbg0KDQpgYGB7ciBzcGF0aWFsX2RlY29uX3J1bn0NCiMgdmVjdG9yIGlkZW50aWZ5aW5nIHB1cmUgdHVtb3Igc2VnbWVudHM6DQojdGFyZ2V0X0RhdGEkaXN0dW1vciA9IHRhcmdldF9EYXRhJEFOTjMgPT0gIkNPUkUiICYgdGFyZ2V0X0RhdGEkQU5OMSA9PSAiUGFuQ0srIg0KcmVzID0gcnVuc3BhdGlhbGRlY29uKG9iamVjdCA9IHRhcmdldF9EYXRhLA0KICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZWx0ID0gInFfbm9ybSIsDQogICAgICAgICAgICAgICAgICAgICAgcmF3X2VsdCA9ICJleHBycyIsDQogICAgICAgICAgICAgICAgICAgICAgI2lzX3B1cmVfdHVtb3IgPSB0YXJnZXRfRGF0YSRpc3R1bW9yLA0KICAgICAgICAgICAgICAgICAgICAgIGNlbGxfY291bnRzID0gdGFyZ2V0X0RhdGEkbnVjbGVpLA0KICAgICAgICAgICAgICAgICAgICAgIFggPSBjdXJyZW50X2NlbGxfcHJvZmlsZSwNCiAgICAgICAgICAgICAgICAgICAgICAjY2VsbG1lcmdlcyA9IHNhZmVUTUUubWF0Y2hlcywgICAgICAgICAgICAgICMgc2FmZVRNRS5tYXRjaGVzIG9iamVjdCwgdXNlZCBieSBkZWZhdWx0DQogICAgICAgICAgICAgICAgICAgICAgI25fdHVtb3JfY2x1c3RlcnMgPSA1LCAgICAgICAgICAgICAgICAgICAgICAjIGhvdyBtYW55IGRpc3RpbmN0IHR1bW9yIHByb2ZpbGVzIHRvIGFwcGVuZCB0byBzYWZlVE1FDQogICAgICAgICAgICAgICAgICAgICAgYWxpZ25fZ2VuZXMgPSBUUlVFKQ0KDQoNCmBgYA0KDQojIDkuMy4xIFNwYXRpYWwgZGVjb252b2x1dGlvbiBoZWF0bWFwcyB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQojIyBBYnVuZGFuY2UNCg0KYGBge3Igc3BhdGlhbF9kZWNvbl9oZWF0bWFwLCBmaWcud2lkdGg9MjUsZmlnLmhlaWdodD0xNX0NCiMgTk9URTogY2hlY2sgY2x1c3RlcmluZy4uIHdoeSBkaWZmZXJlbnQ/DQoNCiNzZXQgZGlzcGxheSB0aHJlc2hvbGRzDQp0aHJlc2ggPC0gc2lnbmlmKHF1YW50aWxlKHJlcyRiZXRhLCAwLjk3KSwgMikNCg0KIyBwbG90IHN0b3JlZCB0byBrZWVwIGNsdXN0ZXJpbmcgZm9yIGxhdGVyDQpwMTwtcGhlYXRtYXAocG1pbih0KHJlcyRiZXRhKSx0aHJlc2gpLA0KICAgICAgICAgI3NjYWxlID0gInJvdyIsIA0KICAgICAgICAgY3V0cmVlX2NvbHMgPSAzLA0KICAgICAgICAgY3V0cmVlX3Jvd3MgPSAyLA0KICAgICAgICAgZm9udHNpemVfcm93ID0gMTIsDQogICAgICAgICBzaG93X3Jvd25hbWVzID0gVFJVRSwgc2hvd19jb2xuYW1lcyA9IFRSVUUsDQogICAgICAgICBhbmdsZV9jb2wgPSAiOTAiLA0KICAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEsDQogICAgICAgICAjY2x1c3RlcmluZ19tZXRob2QgPSAiYXZlcmFnZSIsDQogICAgICAgICAjY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgICNjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICAgbGVnZW5kX2JyZWFrcyA9IGMocm91bmQoc2VxKDAsIHRocmVzaCwgbGVuZ3RoLm91dCA9IDUpKVstNV0sIHRocmVzaCksDQogICAgICAgICBsZWdlbmRfbGFiZWxzID0gYyhyb3VuZChzZXEoMCwgdGhyZXNoLCBsZW5ndGgub3V0ID0gNSkpWy01XSwgcGFzdGUwKCJBYnVuZGFuY2Ugc2NvcmVzLFxudHJ1bmNhdGVkIGFib3ZlIGF0ICIsIHRocmVzaCkpLA0KICAgICAgICAgI2JyZWFrcyA9IHNlcSgwLCA1LCAxKSwNCiAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJ3aGl0ZSIsImRhcmtibHVlIikpKDEwMCksDQogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGNvbG9yX2xpc3QsDQogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IHBEYXRhKHRhcmdldF9EYXRhKVssIGFubl9uYW1lc10NCiAgICAgICAgICkNCiNwMQ0KYGBgDQoNCiMjIFByb3BvcnRpb25hbA0KDQpgYGB7ciBzcGF0aWFsX2RlY29uX3Byb3BoZWF0bWFwLCBmaWcud2lkdGg9MjUsZmlnLmhlaWdodD0xNX0NCiMgcHJvcG9ydGlvbnM6DQpwcm9wcyA8LSByZXBsYWNlKHJlcyRwcm9wX29mX25vbnR1bW9yLCBpcy5uYShyZXMkcHJvcF9vZl9ub250dW1vciksIDApDQoNCnAyPC1waGVhdG1hcCh0KHByb3BzKSwNCiAgICAgICAgICNzY2FsZSA9ICJyb3ciLCANCiAgICAgICAgIGN1dHJlZV9jb2xzID0gMywNCiAgICAgICAgIGN1dHJlZV9yb3dzID0gMiwNCiAgICAgICAgIGZvbnRzaXplX3JvdyA9IDEyLA0KICAgICAgICAgc2hvd19yb3duYW1lcyA9IFRSVUUsIHNob3dfY29sbmFtZXMgPSBUUlVFLA0KICAgICAgICAgYW5nbGVfY29sID0gIjkwIiwNCiAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLA0KICAgICAgICAgI2NsdXN0ZXJpbmdfbWV0aG9kID0gImF2ZXJhZ2UiLA0KICAgICAgICAgI2NsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgICAjY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgIGxlZ2VuZF9icmVha3MgPSByb3VuZChzZXEoMCwgbWF4KHByb3BzKSAqIDAuOTksIGxlbmd0aC5vdXQgPSA1KSwgMiksDQogICAgICAgICBsZWdlbmRfbGFiZWxzID0gYyhyb3VuZChzZXEoMCwgbWF4KHByb3BzKSwgbGVuZ3RoLm91dCA9IDUpLCAyKVstNV0sICJQcm9wb3J0aW9uIG9mIGFsbFxuZml0dGVkIHBvcHVsYXRpb25zIiksDQogICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygid2hpdGUiLCJkYXJrYmx1ZSIpKSgxMDApLA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBjb2xvcl9saXN0LA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBwRGF0YSh0YXJnZXRfRGF0YSlbLCBhbm5fbmFtZXNdKQ0KDQojcDINCg0KYGBgDQoNCiMjIFNjYWxlZA0KDQpgYGB7ciBzcGF0aWFsX2RlY29uX3NjYWxlZGhlYXRtYXAsIGZpZy53aWR0aD0yNSxmaWcuaGVpZ2h0PTE1fQ0KIyBzY2FsZWQgYWJ1bmRhbmNlczoNCmVwc2lsb24gPC0gbWluKHJlcyRiZXRhW3JlcyRiZXRhID4gMF0pDQptYXQgPC0gc3dlZXAocmVzJGJldGEsIDEsIHBtYXgoYXBwbHkocmVzJGJldGEsIDEsIG1heCksIGVwc2lsb24pLCAiLyIpDQoNCnBoZWF0bWFwKHQobWF0KSwNCiAgICAgICAgICNzY2FsZSA9ICJyb3ciLA0KICAgICAgICAgY3V0cmVlX2NvbHMgPSAzLA0KICAgICAgICAgY3V0cmVlX3Jvd3MgPSAzLA0KICAgICAgICAgZm9udHNpemVfcm93ID0gMTIsDQogICAgICAgICBzaG93X3Jvd25hbWVzID0gVFJVRSwgc2hvd19jb2xuYW1lcyA9IFRSVUUsDQogICAgICAgICBhbmdsZV9jb2wgPSAiOTAiLA0KICAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEsDQogICAgICAgICAjY2x1c3RlcmluZ19tZXRob2QgPSAiYXZlcmFnZSIsDQogICAgICAgICAjY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgICNjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICAgbGVnZW5kX2JyZWFrcyA9IGMocm91bmQoc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSA1KSwgMilbLTVdLCAxKSwNCiAgICAgICAgIGxlZ2VuZF9sYWJlbHMgPSBjKHJvdW5kKHNlcSgwLCAxLCBsZW5ndGgub3V0ID0gNSksIDIpWy01XSwgIlNjYWxlZCBhYnVuZGFuY2VcbihyYXRpbyB0byBtYXgpIiksDQogICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygid2hpdGUiLCJkYXJrYmx1ZSIpKSgxMDApLA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBjb2xvcl9saXN0LA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBwRGF0YSh0YXJnZXRfRGF0YSlbLCBhbm5fbmFtZXNdKQ0KDQpgYGANCg0KIyA5LjQgQmFycGxvdHMgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KIyMgYWJ1bmRhbmNlDQoNCmBgYHtyIFNEX2FidW5kYW5jZV9iYXJwbG90LCBmaWcud2lkdGg9MjUsZmlnLmhlaWdodD0xNX0NCiMgZGVmaW5lIHZhcmlhYmxlcyB0byBzaG93IGluIGhlYXRtYXBzOg0KcERhdGEodGFyZ2V0X0RhdGEpJHJlZ2lvbiA8LSANCiAgICBmYWN0b3IocERhdGEodGFyZ2V0X0RhdGEpJEFOTjMsIHVuaXF1ZSh0YXJnZXRfRGF0YSRBTk4zKSkgICAjIE11c3QgYmUgbWFudWFsPw0KcERhdGEodGFyZ2V0X0RhdGEpJGNsYXNzIDwtIA0KICAgIGZhY3RvcihwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMSwgdW5pcXVlKHRhcmdldF9EYXRhJEFOTjEpKSAgICMgTXVzdCBiZSBtYW51YWw/DQoNCnZhcmlhYmxlc190b19wbG90IDwtIGMoInNsaWRlX25hbWUiLCAiQU5OMSIsICJBTk4zIikgICAgICAgICAgICAgICAgICAgICAjIE11c3QgYmUgbWFudWFsPw0KDQojZGVmaW5lIGNvbG9ycw0KDQojIG9ubHkgZm9yIHNhZmVUTUUgY29sb3JzDQojY29sIDwtIGNlbGxjb2xzDQoNCiNjdXN0b20gY2VsbWF0cml4DQojZ2V0IGxhcmdlIG51bWJlciBvZiBjb2xvcnMNCnF1YWxfY29sX3BhbHMgPSBicmV3ZXIucGFsLmluZm9bYnJld2VyLnBhbC5pbmZvJGNhdGVnb3J5ID09ICdxdWFsJyxdDQpjb2xfdmVjdG9yID0gdW5pcXVlKHVubGlzdChtYXBwbHkoYnJld2VyLnBhbCwgcXVhbF9jb2xfcGFscyRtYXhjb2xvcnMsIHJvd25hbWVzKHF1YWxfY29sX3BhbHMpKSkpDQpjZWxsdHlwZXM8LXNhbXBsZShjb2xfdmVjdG9yLGxlbmd0aChjb2xuYW1lcyhyZXMkYmV0YSkpKQ0KbmFtZXMoY2VsbHR5cGVzKTwtY29sbmFtZXMocmVzJGJldGEpDQpjb2w8LWNlbGx0eXBlcw0KDQoNCiN0ZW1wZml4IGZvciBhYmJyZXZpYXRlZCBhbmQgbm93IG1pc21hdGNoaW5nIGFubm90YXRpb25zDQojY2FuIGp1c3QgdXNlIGFubiBkYXRhZnJhbWU/DQojdG1wYW5uPC1jYmluZChBTk4xLEFOTjIsU04pDQp0bXBhbm4gPC0gcERhdGEodGFyZ2V0X0RhdGEpW2Fubl9uYW1lc10NCg0KDQoNCmxheW91dChtYXRyaXgoYygxLCAyLCAzLCAzKSwgbnJvdyA9IDIpLA0KICAgICAgIHdpZHRocyA9IGMoMTAsIDMsIDEwLCAzKSwNCiAgICAgICBoZWlnaHRzID0gYygxLCA4LCAxMCksDQogICAgICApDQoNCnBhcihtYXIgPSBjKDAsIDguMiwgMCwgMC4yKSkNCnBsb3QocDEkdHJlZV9jb2wsIGxhYmVscyA9IEYsIG1haW4gPSAiIiwgeWxhYiA9ICIiLCB5YXh0ID0gIm4iKQ0KcGFyKG1hciA9IGMoMTUsIDgsIDAsIDApKQ0KDQojIGRhdGEgdG8gcGxvdDoNCm1hdCA8LSB0KHJlcyRiZXRhKVssIHAxJHRyZWVfY29sJG9yZGVyXQ0KIyBpbmZlciBzY2FsZSBvZiBuZWdhdGl2ZSB5LWF4aXMgZm9yIGFubm90YXRpb24gY29sb3JiYXJzDQp5bWluIDwtIC1tYXgoY29sU3VtcyhtYXQpKSAqIDAuMTUNCmlmICghaXMuZmluaXRlKHltaW4pKSB7DQogIHltaW4gPC0gMA0KfQ0KDQojIGRyYXcgYmFycGxvdDoNCmJwIDwtIGJhcnBsb3QobWF0LA0KICAgICAgICAgICAgICBjZXgubGFiID0gMS41LA0KICAgICAgICAgICAgICBjb2wgPSBjb2wsIGJvcmRlciA9IE5BLA0KICAgICAgICAgICAgICBjZXgubmFtZXMgPSAxLjEsDQogICAgICAgICAgICAgIGxhcyA9IDIsIG1haW4gPSAiIiwgeWxhYiA9ICJBYnVuZGFuY2Ugc2NvcmVzIiwNCiAgICAgICAgICAgICAgeWxpbSA9IGMoeW1pbiwgbWF4KGNvbFN1bXMobWF0KSkpDQopDQoNCg0KIyBhZGQgY29sb3IgYmFycyBmb3IgYW5ub3RhdGlvbnMNCmZvciAobmFtZSBpbiByZXYodmFyaWFibGVzX3RvX3Bsb3QpKSB7DQogIHlyYW5nZSA8LSBzZXEoeW1pbiAvIDMsIHltaW4sIGxlbmd0aC5vdXQgPSBsZW5ndGgodmFyaWFibGVzX3RvX3Bsb3QpICsgMSlbbWF0Y2gobmFtZSwgdmFyaWFibGVzX3RvX3Bsb3QpICsgYygwLCAxKV0NCiAgeHdpZHRoIDwtIChicFsyXSAtIGJwWzFdKSAvIDINCiAgZm9yIChpIGluIDE6bmNvbChtYXQpKSB7DQogICAgcmVjdChicFtpXSAtIHh3aWR0aCwgeXJhbmdlWzJdLCBicFtpXSArIHh3aWR0aCwgeXJhbmdlWzFdLA0KICAgICAgICAgIyBib3JkZXIgPSBOQSwgY29sID0gYW5uX2NvbG9yc1tbbmFtZV1dW3NlZ21lbnRBbm5vdGF0aW9uc1ttYXRjaChjb2xuYW1lcyhtYXQpW2ldLCBzZWdtZW50QW5ub3RhdGlvbnMkc2VnbWVudElEKSwgbmFtZV1dDQogICAgICAgICAjYm9yZGVyID0gTkEsIGNvbCA9IGFubl9jb2xvcnNbW25hbWVdXVthbm5bcDEkdHJlZV9jb2wkb3JkZXJbaV0sIG5hbWVdXQ0KICAgICAgICAgYm9yZGVyID0gTkEsIGNvbCA9IGNvbG9yX2xpc3RbW25hbWVdXVt0bXBhbm5bY29sbmFtZXMobWF0KVtpXSwgbmFtZV1dDQogICAgKQ0KICB9DQp9DQpheGlzKDIsDQogICAgIGF0ID0gc2VxKHltaW4gLyAzLCB5bWluLCBsZW5ndGgub3V0ID0gbGVuZ3RoKHZhcmlhYmxlc190b19wbG90KSArIDIpWy1jKDEsIGxlbmd0aCh2YXJpYWJsZXNfdG9fcGxvdCkgKyAyKV0sDQogICAgIGxhcyA9IDIsIGxhYmVscyA9IHZhcmlhYmxlc190b19wbG90LCBsdHkgPSAwLCBjZXguYXhpcyA9IDEuMg0KKQ0KDQojZHJhdyBhIGxlZ2VuZDoNCnBhcihtYXIgPSBjKDAuMSwgMC4xLCAwLjEsIDAuMSkpDQpmcmFtZSgpDQpsZWdlbmRjb2xzIDwtIGxlZ2VuZG5hbWVzIDwtIGMoKQ0KI2ZvciAobmFtZSBpbiByZXYobmFtZXMoYW5uX2NvbG9ycykpKSB7DQpmb3IgKG5hbWUgaW4gYygic2xpZGVfbmFtZSIsICJBTk4xIiwgIkFOTjMiKSkgew0KICBsZWdlbmRjb2xzIDwtIGMobGVnZW5kY29scywgTkEsIGNvbG9yX2xpc3RbW25hbWVdXSwgTkEpDQogIGxlZ2VuZG5hbWVzIDwtIGMobGVnZW5kbmFtZXMsIG5hbWUsIG5hbWVzKGNvbG9yX2xpc3RbW25hbWVdXSksIE5BKQ0KfQ0KbGVnZW5kKCJjZW50ZXIiLA0KICAgICAgIHBjaCA9IDE1LA0KICAgICAgIGNleCA9IDEuNSwNCiAgICAgICBjb2wgPSBjKGxlZ2VuZGNvbHMsIHJlcChOQSwgMSksIHJldihjb2wpKSwNCiAgICAgICBsZWdlbmQgPSBjKGxlZ2VuZG5hbWVzLCAiQ2VsbCB0eXBlIiwgcmV2KG5hbWVzKGNvbCkpKSwNCikNCmBgYA0KDQojIyBwcm9wb3J0aW9uYWwNCg0KYGBge3IgU0RfcHJvcF9iYXJwbG90LCBmaWcud2lkdGg9MjUsZmlnLmhlaWdodD0xNX0NCiMgZGVmaW5lIHZhcmlhYmxlcyB0byBzaG93IGluIGhlYXRtYXBzOg0KdmFyaWFibGVzX3RvX3Bsb3QgPC0gYygic2xpZGVfbmFtZSIsICJBTk4xIiwgIkFOTjMiKQ0KDQpsYXlvdXQobWF0cml4KGMoMSwgMiwgMywgMyksIG5yb3cgPSAyKSwNCiAgICAgICB3aWR0aHMgPSBjKDEwLCAzLCAxMCwgMyksDQogICAgICAgaGVpZ2h0cyA9IGMoMSwgOCwgMTApLA0KICAgICAgKQ0KDQpwYXIobWFyID0gYygwLCA4LjIsIDAsIDAuMikpDQpwbG90KHAyJHRyZWVfY29sLCBsYWJlbHMgPSBGLCBtYWluID0gIiIsIHlsYWIgPSAiIiwgeWF4dCA9ICJuIikNCnBhcihtYXIgPSBjKDE1LCA4LCAwLCAwKSkNCg0KIyBkYXRhIHRvIHBsb3Q6DQptYXQgPC0gdChyZXMkcHJvcF9vZl9ub250dW1vcilbLCBwMiR0cmVlX2NvbCRvcmRlcl0NCiAgbWF0IDwtIHJlcGxhY2UobWF0LCBpcy5uYShtYXQpLCAwKQ0KICAjIGluZmVyIHNjYWxlIG9mIG5lZ2F0aXZlIHktYXhpcyBmb3IgYW5ub3RhdGlvbiBjb2xvcmJhcnMNCiAgeW1pbiA8LSAtMC4xNQ0KDQojIGRyYXcgYmFycGxvdDoNCmJwIDwtIGJhcnBsb3QobWF0LA0KICAgICAgICAgICAgICBjZXgubGFiID0gMS41LA0KICAgICAgICAgICAgICBjb2wgPSBjb2wsIGJvcmRlciA9IE5BLA0KICAgICAgICAgICAgICBjZXgubmFtZXMgPSAxLjEsDQogICAgICAgICAgICAgIGxhcyA9IDIsIG1haW4gPSAiIiwgeWxhYiA9ICJQcm9wb3J0aW9uIG9mIGZpdHRlZCBjZWxscyIsDQogICAgICAgICAgICAgIHlsaW0gPSBjKHltaW4sIG1heChjb2xTdW1zKG1hdCkpKQ0KKQ0KDQojIGFkZCBjb2xvciBiYXJzIGZvciBhbm5vdGF0aW9ucw0KZm9yIChuYW1lIGluIHJldih2YXJpYWJsZXNfdG9fcGxvdCkpIHsNCiAgeXJhbmdlIDwtIHNlcSh5bWluIC8gMywgeW1pbiwgbGVuZ3RoLm91dCA9IGxlbmd0aCh2YXJpYWJsZXNfdG9fcGxvdCkgKyAxKVttYXRjaChuYW1lLCB2YXJpYWJsZXNfdG9fcGxvdCkgKyBjKDAsIDEpXQ0KICB4d2lkdGggPC0gKGJwWzJdIC0gYnBbMV0pIC8gMg0KICBmb3IgKGkgaW4gMTpuY29sKG1hdCkpIHsNCiAgICByZWN0KGJwW2ldIC0geHdpZHRoLCB5cmFuZ2VbMl0sIGJwW2ldICsgeHdpZHRoLCB5cmFuZ2VbMV0sDQogICAgICAgICAjIGJvcmRlciA9IE5BLCBjb2wgPSBhbm5fY29sb3JzW1tuYW1lXV1bc2VnbWVudEFubm90YXRpb25zW21hdGNoKGNvbG5hbWVzKG1hdClbaV0sIHNlZ21lbnRBbm5vdGF0aW9ucyRzZWdtZW50SUQpLCBuYW1lXV0NCiAgICAgICAgICNib3JkZXIgPSBOQSwgY29sID0gYW5uX2NvbG9yc1tbbmFtZV1dW2FubltwMiR0cmVlX2NvbCRvcmRlcltpXSwgbmFtZV1dDQogICAgICAgICBib3JkZXIgPSBOQSwgY29sID0gY29sb3JfbGlzdFtbbmFtZV1dW3RtcGFubltjb2xuYW1lcyhtYXQpW2ldLCBuYW1lXV0NCiAgICApDQogIH0NCn0NCmF4aXMoMiwNCiAgICAgYXQgPSBzZXEoeW1pbiAvIDMsIHltaW4sIGxlbmd0aC5vdXQgPSBsZW5ndGgodmFyaWFibGVzX3RvX3Bsb3QpICsgMilbLWMoMSwgbGVuZ3RoKHZhcmlhYmxlc190b19wbG90KSArIDIpXSwNCiAgICAgbGFzID0gMiwgbGFiZWxzID0gdmFyaWFibGVzX3RvX3Bsb3QsIGx0eSA9IDAsIGNleC5heGlzID0gMS4yDQopDQoNCg0KI2RyYXcgYSBsZWdlbmQ6DQpwYXIobWFyID0gYygwLjEsIDAuMSwgMC4xLCAwLjEpKQ0KZnJhbWUoKQ0KbGVnZW5kY29scyA8LSBsZWdlbmRuYW1lcyA8LSBjKCkNCiNmb3IgKG5hbWUgaW4gcmV2KG5hbWVzKGFubl9jb2xvcnMpKSkgew0KZm9yIChuYW1lIGluIGMoInNsaWRlX25hbWUiLCAiQU5OMSIsICJBTk4zIikpIHsNCiAgbGVnZW5kY29scyA8LSBjKGxlZ2VuZGNvbHMsIE5BLCBjb2xvcl9saXN0W1tuYW1lXV0sIE5BKQ0KICBsZWdlbmRuYW1lcyA8LSBjKGxlZ2VuZG5hbWVzLCBuYW1lLCBuYW1lcyhjb2xvcl9saXN0W1tuYW1lXV0pLCBOQSkNCn0NCmxlZ2VuZCgiY2VudGVyIiwNCiAgICAgICBwY2ggPSAxNSwNCiAgICAgICBjZXggPSAxLjQsDQogICAgICAgY29sID0gYyhsZWdlbmRjb2xzLCByZXAoTkEsIDEpLCByZXYoY29sKSksDQogICAgICAgbGVnZW5kID0gYyhsZWdlbmRuYW1lcywgIkNlbGwgdHlwZSIsIHJldihuYW1lcyhjb2wpKSksDQopDQpgYGANCg0KIyAxMSBDTlYNCkNvcHkgTnVtYmVyIFZhcmlhdGlvbiBhbmFseXNpcyB3aXRoIHRoZSBSIHNvZnR3YXJlIHBhY2thZ2UgaW5mZXJDTlYgKGh0dHBzOi8vZ2l0aHViLmNvbS9icm9hZGluc3RpdHV0ZS9pbmZlckNOVi93aWtpKS4NCg0KSW5mZXJDTlYgaXMgdXNlZCB0byBleHBsb3JlIHR1bW9yIHNpbmdsZSBjZWxsIFJOQS1TZXEgZGF0YSB0byBpZGVudGlmeSBldmlkZW5jZSBmb3Igc29tYXRpYyBsYXJnZS1zY2FsZSBjaHJvbW9zb21hbCBjb3B5IG51bWJlciBhbHRlcmF0aW9ucywgc3VjaCBhcyBnYWlucyBvciBkZWxldGlvbnMgb2YgZW50aXJlIGNocm9tb3NvbWVzIG9yIGxhcmdlIHNlZ21lbnRzIG9mIGNocm9tb3NvbWVzLiBUaGlzIGlzIGRvbmUgYnkgZXhwbG9yaW5nIGV4cHJlc3Npb24gaW50ZW5zaXR5IG9mIGdlbmVzIGFjcm9zcyBwb3NpdGlvbnMgb2YgdHVtb3IgZ2Vub21lIGluIGNvbXBhcmlzb24gdG8gYSBzZXQgb2YgcmVmZXJlbmNlICdub3JtYWwnIGNlbGxzLiBBIGhlYXRtYXAgaXMgZ2VuZXJhdGVkIGlsbHVzdHJhdGluZyB0aGUgcmVsYXRpdmUgZXhwcmVzc2lvbiBpbnRlbnNpdGllcyBhY3Jvc3MgZWFjaCBjaHJvbW9zb21lLCBhbmQgaXQgb2Z0ZW4gYmVjb21lcyByZWFkaWx5IGFwcGFyZW50IGFzIHRvIHdoaWNoIHJlZ2lvbnMgb2YgdGhlIHR1bW9yIGdlbm9tZSBhcmUgb3Zlci1hYnVuZGFudCBvciBsZXNzLWFidW5kYW50IGFzIGNvbXBhcmVkIHRvIHRoYXQgb2Ygbm9ybWFsIGNlbGxzLg0KDQpQZXIgcGF0aWVudCBhIGNudiBhbmFseXNpcyB3aXRoIGEgcHJvdmlkZWQgcmVmZXJlbmNlIGdyb3VwLiBUaGUgZ2VuZSBvcmRlciBpcyBtYWRlIGZyb20gdGhlIHByb2plY3RzIC5wa2MgZmlsZS4NCg0KU2VsZWN0IHRoZSBhbm5vdGF0aW9uIHdoZXJlIHRoZSBDTlYgYW5hbHlzaXMgc2hvdWxkIGxvb2sgYXQgc3BlY2lmaWNhbGx5IGFzIGdyb3VwIGFuZCBzcGVjaWZ5IGEgc3ViZ3JvdXAgYXMgZ3JvdXBfZmlsdGVyIGlmIHRoZSBncm91cCBvZiBpbnRlcmVzdCBpcyBhIHN1Ymdyb3VwIGluc2lkZSB0aGUgYW5ub3RhdGlvbi4gRm9yIGV4YW1wbGUgaWYgdGhlIENOViBhbmFseXNpcyBuZWVkcyB0byBiZSBvbmx5IG9uIHR1bW9yIHJlZ2lvbnMgdGhlIGdyb3VwIHdvdWxkIGJlIEFOTlgoQU5OIGNvbHVtbiB3aXRoIHRoZSBpbmZvIGFib3V0IHRoZSB0dW1vciByZWdpb25zKSBhbmQgZ3JvdXBfZmlsdGVyIFBhbkNLKy4gDQpUaGUgYW5ub3RhdGlvbiB0aGF0IHJlZmVyZW5jZXMgdGhlIHBhdGllbnRzIGFzIHBhdGllbnRzLiBJZiB0aGVyZSBhcmUgbm8gcGF0aWVudHMgbGVhdmUgdGhlIHBhcmFtZXRlciBlbXB0eSBhcyAiIi4NClRoZSBhbm5vdGF0aW9uIHRoYXQgc2hvdWxkIGJlIGluY2x1ZGVkIGluIHRoZSByZXN1bHRzIGluY2x1ZGluZyBhIHJlZmVyZW5jZSBzZXQgYXMgY252X3RhcmdldC4NClRoaXMgd2lsbCBjcmVhdGUgcGF0aWVudCBzcGVjaWZpYyBmaWxlcyB3aXRoIGFsbCB0aGUgaW5mb3JtYXRpb24gaW5mZXJDTlYgbmVlZHMuDQoNCmBgYHtyIGNyZWF0ZSBjbnYgZmlsZXMsIGluY2x1ZGU9RkFMU0V9DQojIyMjIyMjIyNQQVJBTUVURVJTIyMjIyMjIyMNCmdyb3VwIDwtICIiDQpncm91cF9maWx0ZXIgPC0gIiINCg0KcGF0aWVudHMgPC0gIiINCg0KY252X3RhcmdldCA8LSAiQU5OMSINCnByaW50KHBhc3RlKCJTaG93IENOViBvbiIsIGNudl90YXJnZXQsICJ3aXRoIHRoZSBzcGVjaWZpY2l0eSBvbiB0aGUgZ3JvdXBzIChpZiBhbnkpOiIsIGdyb3VwLCAiOyIsIGdyb3VwX2ZpbHRlcikpDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KaWYgKCFwYXRpZW50cyA9PSAiIikgew0KICBwYXRpZW50X2xpc3QgPC0gdW5pcXVlKHRhcmdldF9EYXRhW1twYXRpZW50c11dKQ0KfSBlbHNlew0KICBwYXRpZW50X2xpc3QgPC0gImR1bW15Ig0KfQ0KDQpmaWxlIDwtIHJlYWRMaW5lcyhQS0NGaWxlcykNCmZpbGUgPC0gZ3N1YignICcsICcnLA0KICAgICAgICBnc3ViKCciJywgJycsIGZpbGUpKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBGb3JtYXQgYW5kIHJlbW92ZSBzcGFjZQ0KDQpkaXNwbGF5IDwtIGZpbGVbZ3JlcGwoIkRpc3BsYXlOYW1lIiwgZmlsZSwgZml4ZWQgPSBUUlVFKV0gICAgICAgICAjIEdyYWIgZGlzcGxheSBuYW1lcw0KZGlzcGxheSA8LSBnc3ViKCdEaXNwbGF5TmFtZScsICIiLA0KICAgICAgICAgICBnc3ViKCciJywgIiIsDQogICAgICAgICAgIGdzdWIoJzonLCAiIiwNCiAgICAgICAgICAgZ3N1YignLCcsICIiLA0KICAgICAgICAgICBnc3ViKCcgJywgIiIsDQogICAgICAgICAgIGdzdWIoJ18wMScsICIiLCBkaXNwbGF5KSkpKSkpICAgICAgICAgICAgICAgICAgICAgICAgICAjIEdyYWIgb25seSB0aGUgbmFtZXMNCmRpc3BsYXkgPC0gdW5pcXVlKGRpc3BsYXkpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUmVtb3ZlIGR1cGxpY2F0ZXMNCg0KY2hyIDwtIGZpbGVbZ3JlcCgiR2Vub21lQ29vcmRpbmF0ZXMiLCBmaWxlKSsxXSAgICAgICAgICAgICAgICAgICAgIyBHZXQgdGhlIGNociBwb3NpdGlvbnMgdW5kZXIgdGhlIEdlbm9tZUNvb3JkaW5hdGVzIGxpbmUNCmNociA8LWdzdWIoJywnLCAiIiwgY2hyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUmVtb3ZlIHVud2FudGVkIHN5bWJvbHMNCg0KcG9zaXRpb25zIDwtIGRhdGEuZnJhbWUoDQogIG5hbWUgPSBkaXNwbGF5LA0KICBjaHIgPSBjaHIpDQpwb3NpdGlvbnMgPC0gcG9zaXRpb25zWyFncmVwbCgiVGFyZ2V0U2VxdWVuY2UiLCBwb3NpdGlvbnMkY2hyKSxdICAjIFJlbW92ZSBlbnRyaWVzIHdpdGhvdXQgY29vcmRpbmF0ZXMNCg0KcG9zaXRpb25zJGNociA8LSBnc3ViKCc6JywgJy0nLCBwb3NpdGlvbnMkY2hyKSAgICAgICAgICAgICAgICAgICAgIyBGb3JtYXQgZm9yIHNwbGl0dGluZw0KcG9zaXRpb25zJGNociA8LSBzdHJfc3BsaXRfZml4ZWQocG9zaXRpb25zJGNociwgIi0iLCAzKSAgICAgICAgICAgIyBTcGxpdCBpbnRvIGNociwgYmVnaW4sIGFuZCBlbmQgcG9zaXRpb24NCnBvc2l0aW9ucyA8LSBhcy5tYXRyaXgocG9zaXRpb25zKQ0KcG9zaXRpb25zIDwtIGFzLmRhdGEuZnJhbWUocG9zaXRpb25zKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTd2FwIHR5cGVzIHRvIHJlY29nbml6ZSBzcGxpdCBjb2x1bW5zIGFzIHNpbmd1bGFyIGNvbHVtbnMNCg0KcG9zaXRpb25zJG9yZGVyIDwtIGFzLm51bWVyaWMoZ3N1YignY2hyJywgJycsIHBvc2l0aW9ucyRjaHIuMSkpDQpwb3NpdGlvbnMgPC0gcG9zaXRpb25zW29yZGVyKHBvc2l0aW9ucyRvcmRlciwgZGVjcmVhc2luZyA9IEZBTFNFKSwgXSAjIE9yZGVyIHRoZSBjaHJvbW9zb21lcy4NCnBvc2l0aW9ucyRvcmRlciA8LSBOVUxMDQoNCmNvbG5hbWVzKHBvc2l0aW9ucykgPC0gTlVMTA0Kcm93bmFtZXMocG9zaXRpb25zKSA8LSBOVUxMICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBpbmZlckNOViByZXF1aXJlcyBubyBjb2x1bW4gYW5kIHJvdyBuYW1lcw0KDQp3cml0ZS50YWJsZShwb3NpdGlvbnMsICJnZW5lX29yZGVyX0hzX1dUQV92MV9wa2MudHh0IiwNCiAgICAgICAgICAgIHNlcCA9ICJcdCIsDQogICAgICAgICAgICBxdW90ZSA9IEZBTFNFLA0KICAgICAgICAgICAgY29sLm5hbWVzID0gRkFMU0UsDQogICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFdyaXRlIGF3YXkNCg0KDQpmb3IgKHBhdGllbnQgaW4gcGF0aWVudF9saXN0KSB7DQogIGlmIChncm91cF9maWx0ZXIgIT0gIiIpIHsNCiAgICBncm91cF9pbnRlcmVzdCA8LSB0YXJnZXRfRGF0YUBwcm90b2NvbERhdGFAZGF0YVtbIlNhbXBsZUlEIl1dW3RhcmdldF9EYXRhW1tncm91cF1dID09IGdyb3VwX2ZpbHRlciAmIHRhcmdldF9EYXRhW1twYXRpZW50c11dID09IHBhdGllbnRdDQogIH0gZWxzZSBpZiAobGVuZ3RoKHBhdGllbnRfbGlzdCkgPiAxKSB7DQogICAgZ3JvdXBfaW50ZXJlc3QgPC0gdGFyZ2V0X0RhdGFAcHJvdG9jb2xEYXRhQGRhdGFbWyJTYW1wbGVJRCJdXVt0YXJnZXRfRGF0YVtbcGF0aWVudHNdXSA9PSBwYXRpZW50XQ0KICB9IGVsc2Ugew0KICAgIGdyb3VwX2ludGVyZXN0IDwtIHRhcmdldF9EYXRhQHByb3RvY29sRGF0YUBkYXRhW1siU2FtcGxlSUQiXV0NCiAgfQ0KICANCiAgY291bnRzIDwtIHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImV4cHJzIl1dDQogIGNvbG5hbWVzKGNvdW50cykgPC0gZ3N1YignLmRjYycsICcnLGNvbG5hbWVzKGNvdW50cykpDQogIGNvdW50cyA8LSBjb3VudHNbLGNvbG5hbWVzKGNvdW50cykgJWluJSBjKGdyb3VwX2ludGVyZXN0KV0NCiAgd3JpdGUudGFibGUoY291bnRzLCBwYXN0ZTAocGF0aWVudCwgIi5Db3VudHMudHN2IiksIHNlcCA9ICJcdCIpICAgICAgICAgICAgICAgICAgICAjIE1ha2UgcmF3IGNvdW50IGZpbGUNCg0KICBhbm5vdGF0aW9uIDwtIHRhcmdldF9EYXRhQHBoZW5vRGF0YUBkYXRhDQogIGFubm90YXRpb24kU2FtcGxlSUQgPC0gZ3N1YignLmRjYycsJycsIHJvd25hbWVzKGFubm90YXRpb24pKQ0KICBhbm5vdGF0aW9uIDwtIGFubm90YXRpb25bYW5ub3RhdGlvbiRTYW1wbGVJRCAlaW4lIGMoZ3JvdXBfaW50ZXJlc3QpLF0NCiAgI2Fubm90YXRpb24gPC0gYW5ub3RhdGlvbltjKCJTYW1wbGVJRCIsICJzZWdtZW50IildICAgICAgICAgICAgICAgICAgIyBTZWxlY3QgU2FtcGxlSUQgYW5kIHRoZSBuZWVkZWQgYW5ub3RhdGlvbiAoQ05WIHJlcXVpcmVzIFNhbXBsZUlEIGFuZCBvbmx5IDEgYW5ub3RhdGlvbikNCiAgYW5ub3RhdGlvbiA8LSBhbm5vdGF0aW9uW2MoIlNhbXBsZUlEIiwgY252X3RhcmdldCldDQogIHJvd25hbWVzKGFubm90YXRpb24pIDwtIE5VTEwNCiAgY29sbmFtZXMoYW5ub3RhdGlvbikgPC0gTlVMTCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluZmVyQ05WIHJlcXVpcmVzIG5vIGNvbHVtbiBhbmQgcm93IG5hbWVzDQogIHdyaXRlLnRhYmxlKGFubm90YXRpb24sIHBhc3RlMChwYXRpZW50LCAiLkFubm90YXRpb25zLnRzdiIpLA0KICAgICAgICAgICAgICBzZXAgPSAiXHQiLA0KICAgICAgICAgICAgICBxdW90ZSA9IEZBTFNFLA0KICAgICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwNCiAgICAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1ha2UgYW5ub3RhdGlvbiBmaWxlDQp9DQpgYGANCg0KU2VsZWN0IHRoZSByZWZlcmVuY2Ugc2V0IGluIHJlZmVyZW5jZS4gVGhpcyBjYW4gYmUgbW9yZSB0aGFuIG9uZS4gRXZlcnkgcGF0aWVudCB3aXRob3V0IGl0cyBvd24gcmVmZXJlbmNlIHdpbGwgbm90IGJlIGFuYWx5c2VkLg0KDQpgYGB7ciBydW4gQ05WLCBlY2hvPVQsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KIyMjIyMjIyMjUEFSQU1FVEVSUyMjIyMjIyMjDQpyZWZlcmVuY2UgPC0gYygibm9ybWFsIikNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQpmYWlsZWQgPC0gYygpDQpjbnZfbGlzdCA8LSBsaXN0KCkNCg0KZm9yIChwYXRpZW50IGluIHBhdGllbnRfbGlzdCkgew0KICAjIE5hbWUgb3V0cHV0IGZvbGRlcg0KICBvdXRfZGlyIDwtIHBhc3RlMChwYXRpZW50LCAiX0NOViIpDQogIGlmIChzdHJfZGV0ZWN0KHBhc3RlKHJlYWRMaW5lcyhwYXN0ZTAocGF0aWVudCwgIi5Bbm5vdGF0aW9ucy50c3YiKSksIGNvbGxhcHNlID0gJycpLCByZWZlcmVuY2UpID09IEZBTFNFKSB7DQogICAgZmFpbGVkIDwtIGMoZmFpbGVkLCBwYXRpZW50KQ0KICAgIG5leHQNCiAgICB9DQoNCiAgIyBDcmVhdGUgdGhlIGluZmVyY252IG9iamVjdA0KICBpbmZlcmNudl9vYmogPSBDcmVhdGVJbmZlcmNudk9iamVjdChyYXdfY291bnRzX21hdHJpeD0gcGFzdGUwKHBhdGllbnQsICIuQ291bnRzLnRzdiIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uc19maWxlPSBwYXN0ZTAocGF0aWVudCwgIi5Bbm5vdGF0aW9ucy50c3YiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVsaW09Ilx0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9vcmRlcl9maWxlPSAiZ2VuZV9vcmRlcl9Ic19XVEFfdjFfcGtjLnR4dCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICNnZW5lX29yZGVyX2ZpbGU9ICJnZW5jb2RlX3YxOV9nZW5lX3Bvcy50eHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWZfZ3JvdXBfbmFtZXM9IHJlZmVyZW5jZSwgIyBpbnB1dCB0aGUgbm9ybWFsL3JlZmVyZW5jZSBncm91cCBuYW1lcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaHJfZXhjbHVkZSA9IGMoImNock0iKSkjYygiY2hyTSIpKSAjIERlZmF1bHQgZXhjbHVkZXMgY2hyWCwgY2hyWSBhbmQgY2hyTS4gQnkgb25seSBwaWNraW5nIGNock0geW91IGluY2x1ZGUgdGhlIFggYW5kIFkgY2hyb21vc29tZXMuDQoNCiAgIyBwZXJmb3JtIGluZmVyY252IG9wZXJhdGlvbnMgdG8gcmV2ZWFsIGNudiBzaWduYWwuIEZvciBhbGwgb3B0aW9uczogaHR0cHM6Ly9yZHJyLmlvL2dpdGh1Yi9icm9hZGluc3RpdHV0ZS9pbmZlcmNudi9tYW4vcnVuLmh0bWwNCiAgI2Nudl9saXN0W1twYXRpZW50XV0NCiAgaW5mZXJjbnZfb2JqIDwtIGluZmVyY252OjpydW4oaW5mZXJjbnZfb2JqLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1dG9mZj0wLjEsICAjIHVzZSAxIGZvciBzbWFydC1zZXEsIDAuMSBmb3IgMTB4LWdlbm9taWNzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3V0X2Rpcj0gb3V0X2RpciwgICMgZGlyIGlzIGF1dG8tY3JlYXRlZCBmb3Igc3RvcmluZyBvdXRwdXRzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9ieV9ncm91cHM9RkFMU0UsICAgIyBjbHVzdGVyDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVub2lzZT1UUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhNTT1GQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0dW1vcl9zdWJjbHVzdGVyX3BhcnRpdGlvbl9tZXRob2QgPSBjKCJxbm9ybSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuYWx5c2lzX21vZGUgPSAic3ViY2x1c3RlcnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vX3Bsb3Q9VFJVRQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMsZGVidWc9VFJVRQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCg0KICBwbG90X2NudihpbmZlcmNudl9vYmosDQogICAgICAgICAgb3V0X2RpciA9IHBhc3RlMChwYXRpZW50LCAiX0NOViIpLA0KICAgICAgICAgIHRpdGxlID0gImluZmVyQ05WIiwNCiAgICAgICAgICBvYnNfdGl0bGUgPSAiT2JzZXJ2YXRpb25zIChDZWxscykiLA0KICAgICAgICAgIHJlZl90aXRsZSA9ICJSZWZlcmVuY2VzIChDZWxscykiLA0KICAgICAgICAgIGNsdXN0ZXJfYnlfZ3JvdXBzID0gRkFMU0UsDQogICAgICAgICAgY2x1c3Rlcl9yZWZlcmVuY2VzID0gRkFMU0UsDQogICAgICAgICAgcGxvdF9jaHJfc2NhbGUgPSBGQUxTRSwNCiAgICAgICAgICAjY2hyX2xlbmd0aHMgPSBOVUxMLA0KICAgICAgICAgIGtfb2JzX2dyb3VwcyA9IDEsDQogICAgICAgICAgY29udGlnX2NleCA9IDEuNSwNCiAgICAgICAgICAjeC5jZW50ZXIgPSBtZWFuKGluZmVyY252X29iakBleHByLmRhdGEpLA0KICAgICAgICAgIHgucmFuZ2UgPSAiYXV0byIsDQogICAgICAgICAgI2hjbHVzdF9tZXRob2QgPSAid2FyZC5EIiwNCiAgICAgICAgICBvdXRwdXRfZmlsZW5hbWUgPSAiaW5mZXJjbnYiLA0KICAgICAgICAgIG91dHB1dF9mb3JtYXQgPSAicG5nIiwNCiAgICAgICAgICBwbmdfcmVzID0gMzAwDQogICAgICAgICAgKQ0KfQ0KYGBgDQoNCmBgYHtyIHNob3cgQ05WLCBmaWcuaGVpZ2h0PTMwLCBmaWcud2lkdGg9MjV9DQpmb3IgKHBhdGllbnQgaW4gcGF0aWVudF9saXN0KSB7DQogIHByaW50KHBhc3RlKGdyb3VwX2ZpbHRlciwgcGF0aWVudCkpDQogIGlmIChwYXRpZW50ICVpbiUgZmFpbGVkKSB7DQogICAgcHJpbnQoIiBeICAgIFBhdGllbnQgY29udGFpbmVkIG5vIHJlZmVyZW5jZSBncm91cCIpDQogICAgbmV4dA0KICB9DQogIGltZyA8LSByZWFkUE5HKHBhc3RlKHBhc3RlMChwYXRpZW50LCAiX0NOViIpLCAiL2luZmVyY252LnBuZyIsIHNlcCA9ICIiKSkNCiAgZ3JpZDo6Z3JpZC5uZXdwYWdlKCkNCiAgZ3JpZDo6Z3JpZC5yYXN0ZXIoaW1nKQ0KfQ0KYGBgDQoNCiMgMTEuMSBEZW5kcm9ncmFtDQoNCkNsb3NlciBsb29rIGF0IHRoZSBkZW5kcm9ncmFtIGZyb20gdGhlIENOViBhbmFseXNpcyBwZXIgcGF0aWVudCB3aGVyZSB0aGUgbm9kZXMgYXMgd3JpdHRlbiBudW1iZXJzLiBVc2UgdGhlIG51bWJlcnMgdG8gc2VsZWN0IHN1Ymdyb3VwcyBmb3IgZnVydGhlciBhbmFseXNpcyBpbiAndC10ZXN0IG9uIHR3byBzdWJncm91cHMnLg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MjB9DQojb3V0X2RpciA8LSAiVDFfTkFOT18wMTJfQ05WIg0KdHJlZXMgPC0gbGlzdCgpDQoNCmZvciAocGF0aWVudCBpbiBwYXRpZW50X2xpc3QpIHsNCiAgaWYgKHBhdGllbnQgJWluJSBmYWlsZWQpIHsNCiAgICAjcHJpbnQoIlBhdGllbnQgY29udGFpbmVkIG5vIHJlZmVyZW5jZSBncm91cCIpDQogICAgbmV4dA0KICB9DQp0cmVlIDwtIHJlYWQudHJlZShwYXN0ZShwYXRpZW50LCJfQ05WL2luZmVyY252Lm9ic2VydmF0aW9uc19kZW5kcm9ncmFtLnR4dCIsIHNlcCA9ICIiKSkNCm9idiA8LSByZWFkLmNzdihwYXN0ZShwYXRpZW50LCJfQ05WL2luZmVyY252Lm9ic2VydmF0aW9uX2dyb3VwaW5ncy50eHQiLCBzZXAgPSAiIiksIHNlcD0iIikNCnRyZWVzW1twYXRpZW50XV0gPC0gZ2d0cmVlKA0KICB0cmVlLCBsYWRkZXJpemU9RikgKw0KICBnZW9tX3RyZWVzY2FsZSgpICsNCiAgZ2VvbV90aXBsYWIoY29sb3I9b2J2JEFubm90YXRpb24uQ29sb3IsIGhqdXN0PS0uMikgKw0KICBjb29yZF9jYXJ0ZXNpYW4oY2xpcCA9ICdvZmYnKSArDQogIHRoZW1lX3RyZWUyKHBsb3QubWFyZ2luPW1hcmdpbig2LCAyMDAsIDYsIDYpKSArDQogIGdlb21fdGV4dDIoYWVzKGxhYmVsPW5vZGUpLCBoanVzdD0tLjMsIHNpemUgPSAzKSArDQogIGdncGxvdDI6OmxhYnModGl0bGUgPSBwYXRpZW50KQ0KfQ0KDQpncmlkLmFycmFuZ2UoZ3JvYnM9dHJlZXMsbmNvbD0zKQ0KYGBgDQoNCiMjIHQtdGVzdCBjaHIgd2l0aCBncm91cHMNCg0KQ29tcGFyZSB0aGUgY29udHJhc3Qgd2l0aGluIGEgY2hyb21vc29tZS4gU2VsZWN0IGEgY2hyb21vc29tZSwgY29udHJhc3QsIGFuZCBwYXRpZW50IG9mIGludGVyZXN0IHRvIHJ1biBhIHQtdGVzdCBvbi4NCg0KYGBge3J9DQojIyMjIyMjIyNQQVJBTUVURVJTIyMjIyMjIyMNCmNociA8LSAiY2hyWCIgIyBTZWxlY3QgY2hyb21vc29tZSBvZiBpbnRlcmVzdA0KY29udHJhc3QgPC0gYygibm9ybWFsIiwgIkRLRCIpICMgU2VsZWN0IGNvbnRyYXN0DQpwYXRpZW50IDwtICJkdW1teSINCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQpwb3NpdGlvbnMgPC0gYXMuZGF0YS5mcmFtZShwb3NpdGlvbnMpDQpjb2xuYW1lcyhwb3NpdGlvbnMpIDwtIGMoImdlbmUiLCAiY2hyIiwgImJlZ2luIiwgImVuZCIpDQpzZWxlY3RfZ2VuZXMgPC0gcG9zaXRpb25zJGdlbmVbcG9zaXRpb25zJGNociA9PSBjaHJdICMgR3JhYiBjaHIgc3BlY2lmaWMgZ2VuZXMNCg0KYW5ub3RhdGlvbiA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuZGVsaW0ocGFzdGUwKHBhdGllbnQsICIuQW5ub3RhdGlvbnMudHN2IiksIGhlYWRlcj1GQUxTRSkpDQpjb2xuYW1lcyhhbm5vdGF0aW9uKSA8LSBjKCJTYW1wbGVfSUQiLCAiQU5OIikNCnNlbGVjdF9zYW1wbGVzIDwtIGFubm90YXRpb24NCg0KZmlsdGVyX2NvdW50cyA8LSBhcy5kYXRhLmZyYW1lKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImxvZ19xIl1dKQ0KY29sbmFtZXMoZmlsdGVyX2NvdW50cykgPC0gZ3N1YignLmRjYycsJycsIGNvbG5hbWVzKGZpbHRlcl9jb3VudHMpKQ0KZmlsdGVyX2NvdW50cyA8LSBmaWx0ZXJfY291bnRzWyxzZWxlY3Rfc2FtcGxlcyRTYW1wbGVfSURdICMgZmlsdGVyIG91dCBzYW1wbGVzIHRoYXQgYXJlIG5vdCB0aGUgaW50ZXJlc3RpbmcgcmVnaW9uDQoNCnNlbGVjdF9nZW5lcyA8LSBzZWxlY3RfZ2VuZXNbc2VsZWN0X2dlbmVzICVpbiUgcm93bmFtZXMoZmlsdGVyX2NvdW50cyldICMgT25seSB1c2UgZ2VuZXMgdGhhdCBhcmUgYWN0dWFsbHkgaW4gdGhlIGRhdGEgKHBrYyBnZW5lIGZpbGUgaGFzIGFsbCBvZiB0aGVtKQ0KZmlsdGVyX2NvdW50cyA8LSBmaWx0ZXJfY291bnRzW3NlbGVjdF9nZW5lcyxdICMgZmlsdGVyIG91dCBub24gY2hyb21vc29tZSBzcGVjaWZpYyBnZW5lcw0KYGBgDQoNCmBgYHtyIHR0ZXN0IGNociwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTAgfQ0KcGxvdHM8LWxpc3QoKQ0KdGFibGVzPC1saXN0KCkNCmxhYmVsczwtbGlzdCgpDQp0ZXN0PC0idHRlc3QiDQptdGM8LSJCSCINCmNvdW50ZXI9MQ0KDQpsb2dfcV9maWx0ZXIgPC1hcy5kYXRhLmZyYW1lKGZpbHRlcl9jb3VudHMpDQoNCmNvbXBzX2RmPC1kYXRhLmZyYW1lKGNvbXA9JycsdmFsPScnKQ0KDQpmb3IgKGFjdGl2ZV9ncm91cDEgaW4gY29udHJhc3QpIHsNCiAgICBmb3IgKGFjdGl2ZV9ncm91cDIgaW4gY29udHJhc3QpIHsNCg0KICAgICAgI3N1cHJlc3MgcmVkdW5jYW50IGNvbXBhcmVzDQogICAgICBpZihhY3RpdmVfZ3JvdXAxPT1hY3RpdmVfZ3JvdXAyKSB7bmV4dH0NCiAgICAgIGNvbXA8LXBhc3RlKHNvcnQoYyhhY3RpdmVfZ3JvdXAxLGFjdGl2ZV9ncm91cDIpKSxjb2xsYXBzZSA9ICJfIikNCiAgICAgICNwcmludChjb21wKQ0KICAgICAgaWYoY29tcCAlaW4lIGNvbXBzX2RmJGNvbXApIHtuZXh0fQ0KICAgICAgdGVtcF9kZjwtZGF0YS5mcmFtZShjb21wPWNvbXAgLHZhbD0xKQ0KICAgICAgY29tcHNfZGY8LXJiaW5kKGNvbXBzX2RmLHRlbXBfZGYpDQoNCiAgICAgIGxhYmVsc1tbY291bnRlcl1dPC1wYXN0ZShhY3RpdmVfZ3JvdXAxLCIgdnMgIiwgYWN0aXZlX2dyb3VwMikNCiAgICAgIGdyb3VwMTwtbG9nX3FfZmlsdGVyWyxuYW1lcyhhcy5kYXRhLmZyYW1lKGZpbHRlcl9jb3VudHMpKVtzZWxlY3Rfc2FtcGxlcyRBTk49PWFjdGl2ZV9ncm91cDFdXQ0KICAgICAgZ3JvdXAyPC1sb2dfcV9maWx0ZXJbLG5hbWVzKGFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykpW3NlbGVjdF9zYW1wbGVzJEFOTj09YWN0aXZlX2dyb3VwMl1dDQoNCiAgICAgICNydW4gdF90ZXN0cw0KICAgICAgcmVzdWx0czwtYXMuZGF0YS5mcmFtZSAoIGFwcGx5KGxvZ19xX2ZpbHRlciwgMSwgZnVuY3Rpb24oeCkgdC50ZXN0KHhbY29sbmFtZXMoZ3JvdXAxKV0seFtjb2xuYW1lcyhncm91cDIpXSkkcC52YWx1ZSkgKQ0KICAgICAgY29sbmFtZXMocmVzdWx0cyk8LSJyYXdfcF92YWx1ZSINCg0KICAgICAgI211bHRpcGxlX3Rlc3RpbmdfY29ycmVjdGlvbg0KICAgICAgYWRqX3BfdmFsdWU8LSBwLmFkanVzdChyZXN1bHRzJHJhd19wX3ZhbHVlLG1ldGhvZD1tdGMpDQogICAgICByZXN1bHRzPC1jYmluZChyZXN1bHRzLGFkal9wX3ZhbHVlKQ0KDQogICAgICAjY2FsY19mZHINCiAgICAgIEZEUjwtIHAuYWRqdXN0KHJlc3VsdHMkcmF3X3BfdmFsdWUsbWV0aG9kPSJmZHIiKQ0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxGRFIpDQoNCiAgICAgICNmb2xkX2NoYW5nZXMNCiAgICAgICNhcyBiYXNlIGRhdGEgaXMgYWxyZWFkeSBsb2cgdHJhbnNmb3JtZWQsIG1lYW5zIG5lZWQgdG8gYmUgc3VidHJhY3RlZCB0byBnZXQgRkMgaW4gbG9nIHNwYWNlDQogICAgICBmY2hhbmdlczwtYXMuZGF0YS5mcmFtZSggYXBwbHkobG9nX3FfZmlsdGVyLCAxLCBmdW5jdGlvbih4KSAobWVhbih4W2NvbG5hbWVzKGdyb3VwMSldKSAtIG1lYW4oeFtjb2xuYW1lcyhncm91cDIpXSkgKSApICkNCiAgICAgIGNvbG5hbWVzKGZjaGFuZ2VzKTwtIkZDIg0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxmY2hhbmdlcykNCg0KICAgICAgI2FkZCBnZW5lbmFtZXMNCiAgICAgIHJlc3VsdHMkR2VuZTwtcm93bmFtZXMocmVzdWx0cykNCg0KICAgICAgI3NldCBjYXRlZ29yaWVzIGJhc2VkIG9uIFAtdmFsdWUgJiBGRFIgZm9yIHBsb3R0aW5nDQogICAgICByZXN1bHRzJENvbG9yIDwtICJOUyBvciBGQyA8IDAuNSINCiAgICAgIHJlc3VsdHMkQ29sb3JbcmVzdWx0cyRhZGpfcF92YWx1ZSA8IDAuMDVdIDwtICJQIDwgMC4wNSINCiAgICAgIHJlc3VsdHMkQ29sb3JbcmVzdWx0cyRGRFIgPCAwLjA1XSA8LSAiRkRSIDwgMC4wNSINCiAgICAgIHJlc3VsdHMkQ29sb3JbcmVzdWx0cyRGRFIgPCAwLjAwMV0gPC0gIkZEUiA8IDAuMDAxIg0KICAgICAgcmVzdWx0cyRDb2xvclthYnMocmVzdWx0cyRGQykgPCAwLjVdIDwtICJOUyBvciBGQyA8IDAuNSINCiAgICAgIHJlc3VsdHMkQ29sb3IgPC0gZmFjdG9yKHJlc3VsdHMkQ29sb3IsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJOUyBvciBGQyA8IDAuNSIsICJQIDwgMC4wNSIsICJGRFIgPCAwLjA1IiwgIkZEUiA8IDAuMDAxIikpDQoNCiAgICAgICN2dWxjYW5vcGxvdA0KDQogICAgICAjIHBpY2sgdG9wIGdlbmVzIGZvciBlaXRoZXIgc2lkZSBvZiB2b2xjYW5vIHRvIGxhYmVsDQogICAgICAjIG9yZGVyIGdlbmVzIGZvciBjb252ZW5pZW5jZToNCg0KICAgICAgcmVzdWx0cyRpbnZlcnRfUCA8LSAoLWxvZzEwKHJlc3VsdHMkYWRqX3BfdmFsdWUpKSAqIHNpZ24ocmVzdWx0cyRGQykNCiAgICAgIHRvcF9nIDwtIGMoKQ0KICAgICAgdG9wX2cgPC0gYyh0b3BfZywNCiAgICAgICAgICAgICAgICAgcmVzdWx0c1tpbmQsICdHZW5lJ11bDQogICAgICAgICAgICAgICAgICAgb3JkZXIocmVzdWx0c1tpbmQsICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gVFJVRSlbMToxNV1dLA0KICAgICAgICAgICAgICAgICByZXN1bHRzW2luZCwgJ0dlbmUnXVtvcmRlcihyZXN1bHRzW2luZCwgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMToxNV1dKQ0KICAgICAgdG9wX2c8LSB1bmlxdWUodG9wX2cpDQogICAgICByZXN1bHRzIDwtIHJlc3VsdHNbLCAtMSpuY29sKHJlc3VsdHMpXSAjIHJlbW92ZSBpbnZlcnRfUCBmcm9tIG1hdHJpeA0KDQogICAgICAjIEdyYXBoIHJlc3VsdHMNCiAgICAgIHBsb3RzW1tjb3VudGVyXV08LSBnZ3Bsb3QocmVzdWx0cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBGQywgeSA9IC1sb2cxMChhZGpfcF92YWx1ZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IENvbG9yLCBsYWJlbCA9IEdlbmUpKSArDQogICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoMC41LCAtMC41KSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgICBsYWJzKHggPSBwYXN0ZSgiRW5yaWNoZWQgZ2VuZXMgaW4iLCBjaHIsICItIiwgYWN0aXZlX2dyb3VwMiwiIDwtIGxvZzIoRkMpIC0+IEVucmljaGVkIGluIiwgYWN0aXZlX2dyb3VwMSksDQogICAgICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbmNlIikgKw0KICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhgRkRSIDwgMC4wMDFgID0gImRvZGdlcmJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYFAgPCAwLjA1YCA9ICJvcmFuZ2UyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYE5TIG9yIEZDIDwgMC41YCA9ICJncmF5IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMDUpKSkgKw0KICAgICAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHN1YnNldChyZXN1bHRzLCBGRFI8MC4wNSAmICgtMC41PkZDfCBGQz4wLjUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50LnBhZGRpbmcgPSAwLjE1LCBjb2xvciA9ICJibGFjayIsIHNpemU9My41LA0KICAgICAgICAgICAgICAgICAgICAgICAgbWluLnNlZ21lbnQubGVuZ3RoID0gLjEsIGJveC5wYWRkaW5nID0gLjIsIGx3ZCA9IDIsDQogICAgICAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSA1MCkgKw0KICAgICAgICB0aGVtZV9idyhiYXNlX3NpemUgPSAyMCkgKw0KICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICAgICAgICAjZ2d0aXRsZShwYXN0ZShzbGlkZSwiOiAiLCB0ZXN0LCBtdGMsIm11bHRpdGVzdCBjb3JyIikpDQogICAgICAgIGdndGl0bGUocGFzdGUocGF0aWVudCwgIjogIiwgdGVzdCwgbXRjLCJtdWx0aXRlc3QgY29yciIpKQ0KDQogICAgICAjc3RvcmUgdGFibGVzIGZvciBkaXNwbGF5IGxhdGVyDQogICAgICB0YWJsZXNbW2NvdW50ZXJdXTwtcmVzdWx0cw0KDQogICAgICBjb3VudGVyID0gY291bnRlcisxDQogICAgICAjZGF0YXRhYmxlKHN1YnNldChyZXN1bHRzLCBHZW5lICVpbiUgR09JKSwgcm93bmFtZXM9RkFMU0UsY2FwdGlvbiA9IHBhc3RlKCJERSByZXN1bHRzICIsIGFjdGl2ZV9ncm91cDEsIiB2cyAiLCBhY3RpdmVfZ3JvdXAyKSkNCiAgICB9DQogIH0NCg0KZ3JpZC5hcnJhbmdlKGdyb2JzPXBsb3RzLG5jb2w9MikNCmBgYA0KDQojIHQtdGVzdCBvbiB0d28gc3ViZ3JvdXBzDQoNClNlbGVjdCBub2RlcyBmcm9tIHRoZSBkZW5kcm9ncmFtIGFzIHN1Ymdyb3VwcywgaW4gdGhpcyBjYXNlIG5vZGVzIDE4IGFuZCAxNS4gRGVjaWRlIG9uIHRoZSBpbnZvbHZlZCBjaHJvbW9zb21lcyBhbmQgYWRkIHRoZW0gaW50byB0aGUgY2hyX2xpc3QuIFNwZWNpZnkgdGhlIHBhdGllbnQgaW4gcGF0aWVudC4NCg0KYGBge3J9DQojIyMjIyMjIyNQQVJBTUVURVJTIyMjIyMjIyMNCmNocl9saXN0IDwtIGMoImNocjIyIikgIyBDaG9vc2UgbmVlZGVkIGNocm9tb3NvbWVzIG9mIHRoZSB0YXJnZXQgYXJlYS4gVGhpbmsgb2YgaXQgYXMgdGhlIHgtYXhpcw0KcGF0aWVudCA8LSAiZHVtbXkiDQpub2RlX2ludGVyZXN0IDwtIDIzMyAjIENob29zZSBzdWJncm91cCAxDQpub2RlX2ludGVyZXN0MiA8LSAyMTYgIyBDaG9vc2Ugc3ViZ3JvdXAgMg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNCnRyZWUgPC0gcmVhZC50cmVlKHBhc3RlKHBhdGllbnQsIl9DTlYvaW5mZXJjbnYub2JzZXJ2YXRpb25zX2RlbmRyb2dyYW0udHh0Iiwgc2VwID0gIiIpKQ0KdHJlZV9pbmZvIDwtIHRyZWUgJT4lIGFzLnRyZWVkYXRhICU+JSBhc190aWJibGUgIyB0cmFuc2Zvcm0gdHJlZSBpbnRvIGFjY2Vzc2libGUgZGF0YQ0KDQpzYW1wbGVzX2ludGVyZXN0IDwtIG9mZnNwcmluZyh0cmVlX2luZm8sIG5vZGVfaW50ZXJlc3QpICMgR2V0IGxhYmVscyBhdHRhY2hlZCB0byB0aGUgZ3JvdXANCnNhbXBsZXNfaW50ZXJlc3QgPC0gYXMuY2hhcmFjdGVyKG5hLm9taXQoc2FtcGxlc19pbnRlcmVzdCRsYWJlbCkpICMgZm9ybWF0dGluZw0KcGFzdGUoIlN1Ymdyb3VwIDE6ICIsIHNhbXBsZXNfaW50ZXJlc3QpDQoNCnNhbXBsZXNfaW50ZXJlc3QyIDwtIG9mZnNwcmluZyh0cmVlX2luZm8sIG5vZGVfaW50ZXJlc3QyKQ0Kc2FtcGxlc19pbnRlcmVzdDIgPC0gYXMuY2hhcmFjdGVyKG5hLm9taXQoc2FtcGxlc19pbnRlcmVzdDIkbGFiZWwpKQ0KcGFzdGUoIlN1Ymdyb3VwIDI6ICIsIHNhbXBsZXNfaW50ZXJlc3QyKQ0KYGBgDQoNCmBgYHtyfQ0KcG9zaXRpb25zIDwtIGFzLmRhdGEuZnJhbWUocG9zaXRpb25zKQ0KY29sbmFtZXMocG9zaXRpb25zKSA8LSBjKCJnZW5lIiwgImNociIsICJiZWdpbiIsICJlbmQiKSAjIEZvcm1hdHRpbmcNCg0Kc2VsZWN0X2dlbmVzIDwtIHBvc2l0aW9ucyRnZW5lW3Bvc2l0aW9ucyRjaHIgJWluJSBjaHJfbGlzdF0gIyB8IHBvc2l0aW9ucyRjaHIgPT0gY2hyMl0gIyBHcmFiIGNocm9tb3NvbWUgc3BlY2lmaWMgZ2VuZXMNCg0KYW5ub3RhdGlvbiA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuZGVsaW0ocGFzdGUwKHBhdGllbnQsICIuQW5ub3RhdGlvbnMudHN2IiksIGhlYWRlcj1GQUxTRSkpDQpjb2xuYW1lcyhhbm5vdGF0aW9uKSA8LSBjKCJTYW1wbGVfSUQiLCAiQU5OIikNCnNlbGVjdF9zYW1wbGVzIDwtIGFubm90YXRpb25bYW5ub3RhdGlvbiRTYW1wbGVfSUQgJWluJSBzYW1wbGVzX2ludGVyZXN0IHwgYW5ub3RhdGlvbiRTYW1wbGVfSUQgJWluJSBzYW1wbGVzX2ludGVyZXN0MixdICMgR3JhYiBzdWJncm91cCBzcGVjaWZpYyBTYW1wbGUgSURzDQoNCmZpbHRlcl9jb3VudHMgPC0gdGFyZ2V0X0RhdGFAYXNzYXlEYXRhW1sibG9nX3EiXV0NCmNvbG5hbWVzKGZpbHRlcl9jb3VudHMpIDwtIGdzdWIoJy5kY2MnLCcnLCBjb2xuYW1lcyhmaWx0ZXJfY291bnRzKSkNCmZpbHRlcl9jb3VudHMgPC0gZmlsdGVyX2NvdW50c1ssc2VsZWN0X3NhbXBsZXMkU2FtcGxlX0lEXSAjIEZpbHRlciBTYW1wbGUgSUQncw0KDQpzZWxlY3RfZ2VuZXMgPC0gc2VsZWN0X2dlbmVzW3NlbGVjdF9nZW5lcyAlaW4lIHJvd25hbWVzKGZpbHRlcl9jb3VudHMpXSAjIE9ubHkgdXNlIGdlbmVzIHRoYXQgYXJlIGFjdHVhbGx5IGluIHRoZSBkYXRhIChwa2MgZ2VuZSBmaWxlIGhhcyBhbGwgb2YgdGhlbSkNCmZpbHRlcl9jb3VudHMgPC0gZmlsdGVyX2NvdW50c1tzZWxlY3RfZ2VuZXMsXSAjIEZpbHRlciBnZW5lcw0KYGBgDQoNCmBgYHtyIHR0ZXN0IHN1Ymdyb3VwcywgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTB9DQpwbG90czwtbGlzdCgpDQp0YWJsZXM8LWxpc3QoKQ0KbGFiZWxzPC1saXN0KCkNCnRlc3Q8LSJ0dGVzdCINCm10YzwtIkJIIg0KY291bnRlcj0xDQoNCmxvZ19xX2ZpbHRlciA8LWFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykNCg0KY29tcHNfZGY8LWRhdGEuZnJhbWUoY29tcD0nJyx2YWw9JycpDQoNCg0KYWN0aXZlX2dyb3VwMSA8LSBzYW1wbGVzX2ludGVyZXN0ICNzdWJncm91cCAxDQphY3RpdmVfZ3JvdXAyIDwtIHNhbXBsZXNfaW50ZXJlc3QyICNzdWJncm91cCAyDQoNCiMgZm9yIChhY3RpdmVfZ3JvdXAxIGluIGMoInN1YjEiKSkgew0KIyAgICAgZm9yIChhY3RpdmVfZ3JvdXAyIGluIGMoInN1YjIiKSkgew0KDQogICAgICAjc3VwcmVzcyByZWR1bmNhbnQgY29tcGFyZXMNCiAgICAgICNpZihhY3RpdmVfZ3JvdXAxPT1hY3RpdmVfZ3JvdXAyKSB7bmV4dH0NCiAgICAgICNjb21wPC1wYXN0ZShzb3J0KGMoYWN0aXZlX2dyb3VwMSxhY3RpdmVfZ3JvdXAyKSksY29sbGFwc2UgPSAiXyIpDQogICAgICAjcHJpbnQoY29tcCkNCiAgICAgICNpZihjb21wICVpbiUgY29tcHNfZGYkY29tcCkge25leHR9DQogICAgICB0ZW1wX2RmPC1kYXRhLmZyYW1lKGNvbXA9Y29tcCAsdmFsPTEpDQogICAgICBjb21wc19kZjwtcmJpbmQoY29tcHNfZGYsdGVtcF9kZikNCg0KICAgICAgIyBsYWJlbHNbW2NvdW50ZXJdXTwtcGFzdGUoYWN0aXZlX2dyb3VwMSwiIHZzICIsIGFjdGl2ZV9ncm91cDIpDQogICAgICAjIGdyb3VwMTwtbG9nX3FfZmlsdGVyWyxuYW1lcyhhcy5kYXRhLmZyYW1lKGZpbHRlcl9jb3VudHMpKVtzZWxlY3Rfc2FtcGxlcyRBTk49PWFjdGl2ZV9ncm91cDFdXQ0KICAgICAgIyBncm91cDI8LWxvZ19xX2ZpbHRlclssbmFtZXMoYXMuZGF0YS5mcmFtZShmaWx0ZXJfY291bnRzKSlbc2VsZWN0X3NhbXBsZXMkQU5OPT1hY3RpdmVfZ3JvdXAyXV0NCg0KICAgICAgbGFiZWxzW1tjb3VudGVyXV08LXBhc3RlKGFjdGl2ZV9ncm91cDEsIiB2cyAiLCBhY3RpdmVfZ3JvdXAyKQ0KICAgICAgZ3JvdXAxPC1sb2dfcV9maWx0ZXJbLG5hbWVzKGFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykpW3NlbGVjdF9zYW1wbGVzJFNhbXBsZV9JRCAlaW4lIGFjdGl2ZV9ncm91cDFdXQ0KICAgICAgZ3JvdXAyPC1sb2dfcV9maWx0ZXJbLG5hbWVzKGFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykpW3NlbGVjdF9zYW1wbGVzJFNhbXBsZV9JRCAlaW4lIGFjdGl2ZV9ncm91cDJdXQ0KDQogICAgICAjcnVuIHRfdGVzdHMNCiAgICAgIHJlc3VsdHM8LWFzLmRhdGEuZnJhbWUgKCBhcHBseShsb2dfcV9maWx0ZXIsIDEsIGZ1bmN0aW9uKHgpIHQudGVzdCh4W2NvbG5hbWVzKGdyb3VwMSldLHhbY29sbmFtZXMoZ3JvdXAyKV0pJHAudmFsdWUpICkNCiAgICAgIGNvbG5hbWVzKHJlc3VsdHMpPC0icmF3X3BfdmFsdWUiDQoNCiAgICAgICNtdWx0aXBsZV90ZXN0aW5nX2NvcnJlY3Rpb24NCiAgICAgIGFkal9wX3ZhbHVlPC0gcC5hZGp1c3QocmVzdWx0cyRyYXdfcF92YWx1ZSxtZXRob2Q9bXRjKQ0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxhZGpfcF92YWx1ZSkNCg0KICAgICAgI2NhbGNfZmRyDQogICAgICBGRFI8LSBwLmFkanVzdChyZXN1bHRzJHJhd19wX3ZhbHVlLG1ldGhvZD0iZmRyIikNCiAgICAgIHJlc3VsdHM8LWNiaW5kKHJlc3VsdHMsRkRSKQ0KDQogICAgICAjZm9sZF9jaGFuZ2VzDQogICAgICAjYXMgYmFzZSBkYXRhIGlzIGFscmVhZHkgbG9nIHRyYW5zZm9ybWVkLCBtZWFucyBuZWVkIHRvIGJlIHN1YnRyYWN0ZWQgdG8gZ2V0IEZDIGluIGxvZyBzcGFjZQ0KICAgICAgZmNoYW5nZXM8LWFzLmRhdGEuZnJhbWUoIGFwcGx5KGxvZ19xX2ZpbHRlciwgMSwgZnVuY3Rpb24oeCkgKG1lYW4oeFtjb2xuYW1lcyhncm91cDEpXSkgLSBtZWFuKHhbY29sbmFtZXMoZ3JvdXAyKV0pICkgKSApDQogICAgICBjb2xuYW1lcyhmY2hhbmdlcyk8LSJGQyINCiAgICAgIHJlc3VsdHM8LWNiaW5kKHJlc3VsdHMsZmNoYW5nZXMpDQoNCiAgICAgICNhZGQgZ2VuZW5hbWVzDQogICAgICByZXN1bHRzJEdlbmU8LXJvd25hbWVzKHJlc3VsdHMpDQoNCiAgICAgICNzZXQgY2F0ZWdvcmllcyBiYXNlZCBvbiBQLXZhbHVlICYgRkRSIGZvciBwbG90dGluZw0KICAgICAgcmVzdWx0cyRDb2xvciA8LSAiTlMgb3IgRkMgPCAwLjUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkYWRqX3BfdmFsdWUgPCAwLjA1XSA8LSAiUCA8IDAuMDUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkRkRSIDwgMC4wNV0gPC0gIkZEUiA8IDAuMDUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkRkRSIDwgMC4wMDFdIDwtICJGRFIgPCAwLjAwMSINCiAgICAgIHJlc3VsdHMkQ29sb3JbYWJzKHJlc3VsdHMkRkMpIDwgMC41XSA8LSAiTlMgb3IgRkMgPCAwLjUiDQogICAgICByZXN1bHRzJENvbG9yIDwtIGZhY3RvcihyZXN1bHRzJENvbG9yLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiTlMgb3IgRkMgPCAwLjUiLCAiUCA8IDAuMDUiLCAiRkRSIDwgMC4wNSIsICJGRFIgPCAwLjAwMSIpKQ0KDQogICAgICAjdnVsY2Fub3Bsb3QNCg0KICAgICAgIyBwaWNrIHRvcCBnZW5lcyBmb3IgZWl0aGVyIHNpZGUgb2Ygdm9sY2FubyB0byBsYWJlbA0KICAgICAgIyBvcmRlciBnZW5lcyBmb3IgY29udmVuaWVuY2U6DQoNCiAgICAgIHJlc3VsdHMkaW52ZXJ0X1AgPC0gKC1sb2cxMChyZXN1bHRzJGFkal9wX3ZhbHVlKSkgKiBzaWduKHJlc3VsdHMkRkMpDQogICAgICB0b3BfZyA8LSBjKCkNCiAgICAgIHRvcF9nIDwtIGModG9wX2csDQogICAgICAgICAgICAgICAgIHJlc3VsdHNbaW5kLCAnR2VuZSddWw0KICAgICAgICAgICAgICAgICAgIG9yZGVyKHJlc3VsdHNbaW5kLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MTVdXSwNCiAgICAgICAgICAgICAgICAgcmVzdWx0c1tpbmQsICdHZW5lJ11bb3JkZXIocmVzdWx0c1tpbmQsICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gRkFMU0UpWzE6MTVdXSkNCiAgICAgIHRvcF9nPC0gdW5pcXVlKHRvcF9nKQ0KICAgICAgcmVzdWx0cyA8LSByZXN1bHRzWywgLTEqbmNvbChyZXN1bHRzKV0gIyByZW1vdmUgaW52ZXJ0X1AgZnJvbSBtYXRyaXgNCg0KICAgICAgIyBHcmFwaCByZXN1bHRzDQogICAgICAjcGxvdHNbW2NvdW50ZXJdXTwtIGdncGxvdChyZXN1bHRzLA0KICAgICAgcCA8LSBnZ3Bsb3QocmVzdWx0cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBGQywgeSA9IC1sb2cxMChhZGpfcF92YWx1ZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IENvbG9yLCBsYWJlbCA9IEdlbmUpKSArDQogICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoMC41LCAtMC41KSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgICBsYWJzKHggPSBwYXN0ZSgiRW5yaWNoZWQgZ2VuZXMgZnJvbSIsIGNocl9saXN0LCAiaW4iLCAic3ViZ3JvdXAgMiIsIiA8LSBsb2cyKEZDKSAtPiBFbnJpY2hlZCBpbiIsICJzdWJncm91cCAxIiksDQogICAgICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbmNlIikgKw0KICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhgRkRSIDwgMC4wMDFgID0gImRvZGdlcmJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYFAgPCAwLjA1YCA9ICJvcmFuZ2UyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYE5TIG9yIEZDIDwgMC41YCA9ICJncmF5IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMDUpKSkgKw0KICAgICAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHN1YnNldChyZXN1bHRzLCBGRFI8MC4wNSAmICgtMC41PkZDfCBGQz4wLjUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50LnBhZGRpbmcgPSAwLjE1LCBjb2xvciA9ICJibGFjayIsIHNpemU9My41LA0KICAgICAgICAgICAgICAgICAgICAgICAgbWluLnNlZ21lbnQubGVuZ3RoID0gLjEsIGJveC5wYWRkaW5nID0gLjIsIGx3ZCA9IDIsDQogICAgICAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSA1MCkgKw0KICAgICAgICB0aGVtZV9idyhiYXNlX3NpemUgPSAyMCkgKw0KICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICAgICAgICAjZ2d0aXRsZShwYXN0ZShzbGlkZSwiOiAiLCB0ZXN0LCBtdGMsIm11bHRpdGVzdCBjb3JyIikpDQogICAgICAgIGdndGl0bGUocGFzdGUocGF0aWVudCwgIjogIiwgdGVzdCwgbXRjLCJtdWx0aXRlc3QgY29yciIpKQ0KDQogICAgICAjc3RvcmUgdGFibGVzIGZvciBkaXNwbGF5IGxhdGVyDQogICAgICB0YWJsZXNbW2NvdW50ZXJdXTwtcmVzdWx0cw0KDQogICAgICBjb3VudGVyID0gY291bnRlcisxDQogICAgICAjZGF0YXRhYmxlKHN1YnNldChyZXN1bHRzLCBHZW5lICVpbiUgR09JKSwgcm93bmFtZXM9RkFMU0UsY2FwdGlvbiA9IHBhc3RlKCJERSByZXN1bHRzICIsIGFjdGl2ZV9ncm91cDEsIiB2cyAiLCBhY3RpdmVfZ3JvdXAyKSkNCiAgIyAgIH0NCiAgIyB9DQoNCiNnZ3Bsb3RseShwKQ0KcA0KI2dyaWQuYXJyYW5nZShncm9icz1wbG90cyxuY29sPTIpDQpgYGANCg0KIyAxMiBDb2RlICYgVmVyc2lvbnMNCg0KUGlwZWxpbmV2ZXJzaW9uOiB2MSBiYXNlZCBvbjoNCjxodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvd29ya2Zsb3dzL3ZpZ25ldHRlcy9HZW9NeFdvcmtmbG93cy9pbnN0L2RvYy9HZW9teFRvb2xzX1JOQS1OR1NfQW5hbHlzaXMuaHRtbD4NCg0KVGhlIHVuZGVybHlpbmcgY29kZSBjYW4gYmUgZG93bmxvYWRlZCBmcm9tIHRoZSAnQ29kZScsIGJ1dHRvbiBvbiB0aGUgdG9wDQpvZiB0aGlzIHBhZ2UuIENob29zZSBvcHRpb24gJ2Rvd25sb2FkIFJtZCcgdG8gZG93bmxvYWQgdGhlIGZ1bGwgcGlwZWxpbmUNCndoaWNoIGNhbiBiZSBvcGVuZWQgaW4gUiBvciBSc3R1ZGlvLiBTb21lIGZpbGVwYXRocyBhcmUgaGFyZGNvZGVkIGFuZA0KbmVlZCB0byBiZSBjaGFuZ2VkIGFjY29yZGluZyB0byB5b3VyIHNldHVwLg0KDQojIDEyLjEgUiBzZXNzaW9uIGluZm9ybWF0aW9uDQoNCmBgYHtyIHNlc3Npb25faW5mb30NCnNlc3Npb25JbmZvKCkNCmBgYA0KDQojIyAxMi4yIFJlZmVyZW5jZXMNCg0KIVtdKGh0dHA6Ly91c2VxLm5sL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDIyLzEyL2RlY29yYXRpb24tc3Ryb2tlLWZsYXQucG5nKQ0KDQpgYGB7cn0NCmtuaXRyOjprbml0X2V4aXQoKQ0KYGBgDQoNCiMgOS4wIHJlbG9hZCBkYXRhDQoNCmBgYHtyIHNwYXRpYWxfZGVjb25fcHJlcGFyZX0NCiNyZWxvYWQgZGF0YQ0KIyBEYXRhIDwtDQojICAgcmVhZE5hbm9TdHJpbmdHZW9NeFNldChkY2NGaWxlcyA9IERDQ0ZpbGVzLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgcGtjRmlsZXMgPSBQS0NGaWxlcywNCiMgICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YUZpbGUgPSBTYW1wbGVBbm5vdGF0aW9uRmlsZSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YVNoZWV0ID0gIlNoZWV0MSIsDQojICAgICAgICAgICAgICAgICAgICAgICAgICBwaGVub0RhdGFEY2NDb2xOYW1lID0gIlNhbXBsZV9JRCIsDQojICAgICAgICAgICAgICAgICAgICAgICAgICBwcm90b2NvbERhdGFDb2xOYW1lcyA9IGMoImFvaSIsICJyb2kiKSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cGVyaW1lbnREYXRhQ29sTmFtZXMgPSBjKCJwYW5lbCIpKQ0KIyANCiMgcGtjcyA8LSBhbm5vdGF0aW9uKERhdGEpDQojIG1vZHVsZXMgPC0gZ3N1YigiLnBrYyIsICIiLCBwa2NzKQ0KIyANCiMgI3NoaWZ0IGFueSBleHByZXNzaW9uIGNvdW50cyB3aXRoIGEgdmFsdWUgb2YgMCB0byAxIHRvIGVuYWJsZSBpbiBkb3duc3RyZWFtIHRyYW5zZm9ybWF0aW9ucy4NCiMgRGF0YSA8LSBzaGlmdENvdW50c09uZShEYXRhLCB1c2VEQUxvZ2ljID0gVFJVRSkNCiMgDQojICNjb2xsYXBzX3RhcmdldHMNCiMgdGFyZ2V0X0RhdGEgPC0gYWdncmVnYXRlQ291bnRzKERhdGEpDQojIGRpbSh0YXJnZXRfRGF0YSkNCiMgDQojICNub3JtYWxpemUNCiMgdGFyZ2V0X0RhdGEgPC0gbm9ybWFsaXplKHRhcmdldF9EYXRhICwgZGF0YV90eXBlID0gIlJOQSIsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9tZXRob2QgPSAicXVhbnQiLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpcmVkUXVhbnRpbGUgPSAuNzUsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9FbHQgPSAicV9ub3JtIikNCmBgYA0KDQojIDkuNCBSdW4gYWR2YW5jZWQgc3BhdGlhbCBkZXZvbmNvbHV0aW9uDQoNCmBgYHtyfQ0KIyB2ZWN0b3IgaWRlbnRpZnlpbmcgcHVyZSB0dW1vciBzZWdtZW50czoNCnRhcmdldF9EYXRhJGlzdHVtb3IgPSB0YXJnZXRfRGF0YSRBTk4yID09ICJFcGl0aGVsaXVtIg0KDQojIHJ1biBzcGF0aWFsZGVjb24gd2l0aCBhbGwgdGhlIGJlbGxzIGFuZCB3aGlzdGxlczoNCnJlc3RpbHMgPSBydW5zcGF0aWFsZGVjb24ob2JqZWN0ID0gdGFyZ2V0X0RhdGEsDQogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZWx0ID0gInFfbm9ybSIsICAgICAgICAgICAgICAgICAgICAjIG5vcm1hbGl6ZWQgZGF0YQ0KICAgICAgICAgICAgICAgICAgICAgICAgICByYXdfZWx0ID0gImV4cHJzIiwgICAgICAgICAgICAgICAgICAgICAgIyBleHBlY3RlZCBiYWNrZ3JvdW5kIGNvdW50cyBmb3IgZXZlcnkgZGF0YSBwb2ludCBpbiBub3JtDQogICAgICAgICAgICAgICAgICAgICAgICAgIFggPSBzYWZlVE1FLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHNhZmVUTUUgbWF0cml4LCB1c2VkIGJ5IGRlZmF1bHQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY2VsbG1lcmdlcyA9IHNhZmVUTUUubWF0Y2hlcywgICAgICAgICAgICMgc2FmZVRNRS5tYXRjaGVzIG9iamVjdCwgdXNlZCBieSBkZWZhdWx0DQogICAgICAgICAgICAgICAgICAgICAgICAgIGNlbGxfY291bnRzID0gdGFyZ2V0X0RhdGEkbnVjbGVpLCAgICAgICMgbnVjbGVpIGNvdW50cywgdXNlZCB0byBlc3RpbWF0ZSB0b3RhbCBjZWxscw0KICAgICAgICAgICAgICAgICAgICAgICAgICBpc19wdXJlX3R1bW9yID0gdGFyZ2V0X0RhdGEkaXN0dW1vciwgICAjIGlkZW50aXRpZXMgb2YgdGhlIFR1bW9yIHNlZ21lbnRzL29ic2VydmF0aW9ucw0KICAgICAgICAgICAgICAgICAgICAgICAgICBuX3R1bW9yX2NsdXN0ZXJzID0gNSkgICAgICAgICAgICAgICAgICAgIyBob3cgbWFueSBkaXN0aW5jdCB0dW1vciBwcm9maWxlcyB0byBhcHBlbmQgdG8gc2FmZVRNRQ0KDQojc3RyKHBEYXRhKHJlc3RpbHMpKQ0KaGVhdG1hcChzd2VlcChyZXN0aWxzQGV4cGVyaW1lbnREYXRhQG90aGVyJFNwYXRpYWxEZWNvbk1hdHJpeCwgMSwgYXBwbHkocmVzdGlsc0BleHBlcmltZW50RGF0YUBvdGhlciRTcGF0aWFsRGVjb25NYXRyaXgsIDEsIG1heCksICIvIiksDQogICAgICAgICBsYWJSb3cgPSBOQSwgbWFyZ2lucyA9IGMoMTAsIDUpKQ0KDQpgYGANCg0KPCEtLSAjIDkuMy4yIFBsb3R0aW5nIGRlY29udm9sdXRpb24gcmVzdWx0cyAtLT4NCg0KPCEtLSBgYGB7ciwgZmlnLndpZHRoPTE1LGZpZy5oZWlnaHQ9N30gLS0+DQoNCjwhLS0gIyBGb3IgcmVmZXJlbmNlLCBzaG93IHRoZSBUSUxzIGNvbG9yIGRhdGEgb2JqZWN0IHVzZWQgYnkgdGhlIHBsb3R0aW5nIGZ1bmN0aW9ucyAgLS0+DQoNCjwhLS0gIyB3aGVuIHNhZmVUTUUgaGFzIGJlZW4gdXNlZDogLS0+DQoNCjwhLS0gZGF0YSgiY2VsbGNvbHMiKSAtLT4NCg0KPCEtLSAjY2VsbGNvbHMgLS0+DQoNCjwhLS0gbyA9IGhjbHVzdChkaXN0KHQocmVzJGNlbGwuY291bnRzJGNlbGwuY291bnRzKSkpJG9yZGVyIC0tPg0KDQo8IS0tIGxheW91dChtYXRyaXgoYygxLCAyKSwgMSksIHdpZHRocyA9IGMoNywgMykpIC0tPg0KDQo8IS0tIFRJTF9iYXJwbG90KHQocmVzJGNlbGwuY291bnRzJGNlbGwuY291bnRzWywgb10pLCAtLT4NCg0KPCEtLSAgICAgICAgICAgICBob3JpeiA9IFRSVUUsIGRyYXdfbGVnZW5kID0gVFJVRSwgY2V4Lm5hbWVzID0gMC45KSAtLT4NCg0KPCEtLSAjcGFyKG1hcj1jKDIsIDE1LCAyLCAyKSkgLS0+DQoNCjwhLS0gIyBvciB0aGUgcHJvcG9ydGlvbnMgb2YgY2VsbHM6IC0tPg0KDQo8IS0tIHRlbXAgPSByZXBsYWNlKHJlcyRwcm9wX29mX25vbnR1bW9yLCBpcy5uYShyZXMkcHJvcF9vZl9ub250dW1vciksIDApIC0tPg0KDQo8IS0tIG8gPSBoY2x1c3QoZGlzdCh0ZW1wW3JlcyRBTk4yID09ICJDRDQ1IixdKSkkb3JkZXIgLS0+DQoNCjwhLS0gVElMX2JhcnBsb3QodChyZXMkcHJvcF9vZl9hbGwpLCAgLS0+DQoNCjwhLS0gICAgICAgICAgICAgaG9yaXogPSBUUlVFLCBkcmF3X2xlZ2VuZCA9IFRSVUUsIGNleC5uYW1lcyA9IDAuOSkgLS0+DQoNCjwhLS0gYGBgIC0tPg0KDQojIDkuMy4zIEZsb3JldHMgb2YgU3BhdGlhbCBkZWNvbnZvbHV0aW9uDQoNClRoZSBzZWNvbmQgZnVuY3Rpb24gaXMgImZsb3JldHMiLCB1c2VkIGZvciBwbG90dGluZyBjZWxsIGFidW5kYW5jZXMgYXRvcA0Kc29tZSAyLUQgcHJvamVjdGlvbi4gSGVyZSwgd2UnbGwgcGxvdCBjZWxsIGFidW5kYW5jZXMgYXRvcCB0aGUgZmlyc3QgMg0KcHJpbmNpcGFsIGNvbXBvbmVudHMgb2YgdGhlIGRhdGE6DQoNCmBgYHtyfQ0KeHkgPC0gcmVhZF9leGNlbCgiTDovcGtsb29zdGVybWFuL0tpZG5leV9vcmdhbl9hdGxhcy9hbm5vdGF0aW9uL09yZ2FuQXRsYXNfS2lkbmV5Lnhsc3giLCBzaGVldCA9ICJTZWdtZW50UHJvcGVydGllcyIpDQp4eSRuZXd4IDwtICh4eSRST0lDb29yZGluYXRlWCAtIHh5JFNjYW5PZmZzZXRYKQ0KeHkkbmV3eSA8LSAoeHkkUk9JQ29vcmRpbmF0ZVkgLSB4eSRTY2FuT2Zmc2V0WSkNCnh5IDwtIHh5WyxjKCJTZWdtZW50RGlzcGxheU5hbWUiLCAibmV3eCIsICJuZXd5IildDQp4eVsiYW9pIl0gPC0geHlbIlNlZ21lbnREaXNwbGF5TmFtZSJdDQphbm5vdCA8LSB0YXJnZXRfRGF0YUBwcm90b2NvbERhdGFAZGF0YQ0KYW5ub3QgPC0gYW5ub3QgJT4lIGlubmVyX2pvaW4oeHksIGJ5ID0gJ2FvaScpICMlPiUgc2VsZWN0KERlc2NyaXB0aW9uLCBJRCwgZ2VuZUlELCBsZWFkaW5nRWRnZSwgcGFkaikNCmFubm90JHggPC0gYW5ub3QkbmV3eA0KYW5ub3QkeSA8LSBhbm5vdCRuZXd5DQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD0yNSxmaWcuaGVpZ2h0PTE1LCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjUwJSJ9DQojUDM2ID0gY3JlYXRlUGFsZXR0ZShsZW5ndGgocm93bmFtZXModChyZXMkYmV0YSkpKSwgIGMoIiNmZjAwMDAiLCAiIzAwZmYwMCIsICIjMDAwMGZmIikpDQoNCnhsaW0gPC0gbWF4KHJlc0BwaGVub0RhdGFAZGF0YSR4KQ0KeWxpbSA8LSBtYXgocmVzQHBoZW5vRGF0YUBkYXRhJHkpDQoNCmluZCA8LSBwRGF0YShyZXMpJHNsaWRlX25hbWUgPT0gImh1X2tpZG5leV8wMDEiDQpyZXMyIDwtIHJlc1ssaW5kXQ0KDQojYSA8LSByZXMyWyFyZXMyQHBoZW5vRGF0YUBkYXRhW1siYmV0YSJdXT09MF0NCg0KI3NsaWRlX2xpc3RbWzRdXSA8LSBmbGlwWShzbGlkZV9saXN0W1s0XV0pDQoNCmZsdW9yIDwtIGZsdW9yTGVnZW5kKG92ZXJsYXkgPSBzbGlkZV9saXN0W1s0XV0sIG5yb3cgPSA0LCB0ZXh0U2l6ZSA9IDUsIGFscGhhID0gMC4yNSkNCg0KaW0gPC0gcGxvdFNwYXRpYWxPdmVybGF5KG92ZXJsYXkgPSBzbGlkZV9saXN0W1s0XV0sIGxlZ2VuZCA9IEZBTFNFLCBoaVJlcyA9IEZBTFNFLCBjb3JuZXIgPSAiYm90dG9tcmlnaHQiLCANCiAgICAgICAgICAgICAgICAgICBzY2FsZUJhcldpZHRoID0gMC41LCB0ZXh0RGlzdGFuY2UgPSA1LCBzY2FsZUJhckNvbG9yID0gIndoaXRlIiwgaW1hZ2UgPSBUUlVFKSArIA0KICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gcGFzdGUoIldob2xlIHNsaWRlIE9NRS1USUZGIiwgc2xpZGVOYW1lKHNsaWRlX2xpc3RbWzRdXSkpKQ0KDQojcGxvdCgwLCB4bGltID0gYygwLCB4bGltKzQwMDAwKSwgeWxpbSA9IGMoMCwgeWxpbSksIGF4ZXMgPSBGQUxTRSkNCg0KY293cGxvdDo6Z2dkcmF3KCkgKw0KICBjb3dwbG90OjpkcmF3X3Bsb3QoaW0sIHggPSAtMC4zLCB5ID0gMCkgKw0KICBjb3dwbG90OjpkcmF3X3Bsb3QoZmx1b3IsIHNjYWxlID0gMC4xMiwgeCA9IDAuMSwgeSA9IDApICMrICMsIHNjYWxlID0gMC4xMiwgeCA9IC0wLjMsIHkgPSAtMC4yNSkNCg0KDQpmbG9yZXRzKA0KICB4ID0gcmVzMkBwaGVub0RhdGFAZGF0YSR4LCMgKyA0MDAwMCwNCiAgeSA9IHJlczJAcGhlbm9EYXRhQGRhdGEkeSwNCiAgY29sID0gUDM2LA0KICAjY29sID0gMTpsZW5ndGgocm93bmFtZXModChhJGJldGEpKSksDQogIHhsaW0gPSBjKDAseGxpbSs0MDAwMCksDQogIHlsaW0gPSBjKDAseWxpbSksDQogIGIgPSB0KGEkYmV0YSksDQogIHJlc2NhbGUuYnkuc3FydCA9IFRSVUUsDQogIGFkZCA9IEZBTFNFLA0KICBjZXggPSAxLCB4bGFiID0gIiIsIHlsYWIgPSAiIikNCg0KbGVnZW5kKCJyaWdodCIsDQogICAgICAgI2ZpbGwgPSAxOmxlbmd0aChyb3duYW1lcyh0KGEkYmV0YSkpKSwNCiAgICAgICBmaWxsID0gUDM2LA0KICAgICAgIGxlZ2VuZCA9IHJvd25hbWVzKHQoYSRiZXRhKSksIGNleCA9IDEuNSkNCmBgYA0KDQojIDkuNCBjb21iaW5pbmcgY2VsbHR5cGVzDQoNCldoZW4gdHdvIGNlbGwgdHlwZXMgYXJlIHRvbyBzaW1pbGFyLCB0aGUgZXN0aW1hdGlvbiBvZiB0aGVpciBhYnVuZGFuY2VzDQpiZWNvbWVzIHVuc3RhYmxlLiBIb3dldmVyLCB0aGVpciBzdW0gY2FuIHN0aWxsIGJlIGVzdGltYXRlZCBlYXNpbHkuIFRoZQ0KZnVuY3Rpb24gImNvbGxhcHNlQ2VsbFR5cGVzIiB0YWtlcyBhIGRlY29udm9sdXRpb24gcmVzdWx0cyBvYmplY3QgYW5kDQpjb2xsYXBzZXMgYW55IGNvbHNlbHktcmVsYXRlZCBjZWxsIHR5cGVzIHlvdSB0ZWxsIGl0IHRvDQoNCmBgYHtyfQ0KbWF0Y2hpbmcgPSBsaXN0KCkNCm1hdGNoaW5nJG15ZWxvaWQgPSBjKCAibWFjcm9waGFnZXMiLCAibW9ub2N5dGVzIiwgIm1EQ3MiKQ0KbWF0Y2hpbmckVC5OSyA9IGMoIkNENC5ULmNlbGxzIiwiQ0Q4LlQuY2VsbHMiLCAiVHJlZyIsICJOSyIpDQptYXRjaGluZyRCID0gYygiQiIpDQptYXRjaGluZyRtYXN0ID0gYygibWFzdCIpDQptYXRjaGluZyRuZXV0cm9waGlscyA9IGMoIm5ldXRyb3BoaWxzIikNCm1hdGNoaW5nJHN0cm9tYSA9IGMoImVuZG90aGVsaWFsLmNlbGxzIiwgImZpYnJvYmxhc3RzIikNCg0KY29sbGFwc2VkID0gY29sbGFwc2VDZWxsVHlwZXMoZml0ID0gcmVzdGlscywgbWF0Y2hpbmcgPSBtYXRjaGluZykNCg0KaGVhdG1hcChjb2xsYXBzZWQkYmV0YSwgY2V4Um93ID0gMC44NSwgY2V4Q29sID0gMC43NSkNCmBgYA0KDQojIExpZ2FuZC1yZWNlcHRvciBhbmFseXNlDQoNCmBgYHtyfQ0KI2RldnRvb2xzOjppbnN0YWxsX2dpdGh1Yigic2FleXNsYWIvbmljaGVuZXRyIikNCiNpbnN0YWxsLnBhY2thZ2VzKCdjaXJjbGl6ZScpDQpsaWJyYXJ5KG5pY2hlbmV0cikNCmxpYnJhcnkoU2V1cmF0KQ0KbGlicmFyeShjaXJjbGl6ZSkNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTB9DQojIEdldCBjb3VudHMgZGF0YQ0KY291bnRzIDwtIGFzLmRhdGEuZnJhbWUodGFyZ2V0X0RhdGFAYXNzYXlEYXRhW1sicV9ub3JtIl1dKQ0KY291bnRzJFggPC0gcm93bmFtZXMoY291bnRzKQ0KI3Jvd25hbWVzKGNvdW50cykgPC0gTlVMTA0KI2NvdW50cyA8LSBhcy5tYXRyaXgoY291bnRzKQ0KDQojIEdldCBtZXRhIGRhdGENCm1ldGEgPC0gcERhdGEodGFyZ2V0X0RhdGEpDQptZXRhIDwtIG1ldGFbYygiQU5OMSIsICJBTk4yIiwgInNsaWRlX25hbWUiKV0NCm1ldGEkU2FtcGxlSUQgPC0gcHJvdG9jb2xEYXRhKHRhcmdldF9EYXRhKVtbIlNhbXBsZUlEIl1dDQptZXRhJHJvaSA8LSBwcm90b2NvbERhdGEodGFyZ2V0X0RhdGEpW1sicm9pIl1dDQoNCiMgY3JlYXRlIFNldXJhdCBmb3IgYmV0dGVyIG5pY2hlIGNvb3BlcmF0aW9uDQpTZXVyYXQuRGF0YSA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gY291bnRzLCBtZXRhLmRhdGEgPSBtZXRhKSANCg0KIyMgQ2hlY2sgbWV0YSBkYXRhDQojU2V1cmF0LkRhdGFAbWV0YS5kYXRhICU+JSBoZWFkKCkNCiNTZXVyYXQuRGF0YUBtZXRhLmRhdGEkUmVnaW9uICU+JSB0YWJsZSgpIA0KDQojIyBDaGFuZ2UgaWRlbnRzIHRvIGNvcnJlY3QgYW5ub3RhdGlvbg0KU2V1cmF0LkRhdGEgPC0gU2V0SWRlbnQoU2V1cmF0LkRhdGEsIHZhbHVlID0gIkFOTjEiKQ0KI1NldXJhdC5EYXRhQGFjdGl2ZS5pZGVudA0KDQojIyMjIyMjIFJlYWQgaW4gbGlnYW5kLXRhcmdldCBwcmlvciBtb2RlbCwgbGlnYW5kLXJlY2VwdG9yIG5ldHdvcmsgYW5kIHdlaWdodGVkIGludGVncmF0ZWQgbmV0d29ya3MNCmxpZ2FuZF90YXJnZXRfbWF0cml4ID0gcmVhZFJEUyh1cmwoImh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmQvMzI2MDc1OC9maWxlcy9saWdhbmRfdGFyZ2V0X21hdHJpeC5yZHMiKSkNCiNsaWdhbmRfdGFyZ2V0X21hdHJpeFsxOjUsMTo1XSAjIHRhcmdldCBnZW5lcyBpbiByb3dzLCBsaWdhbmRzIGluIGNvbHVtbnMNCg0KbHJfbmV0d29yayA9IHJlYWRSRFModXJsKCJodHRwczovL3plbm9kby5vcmcvcmVjb3JkLzMyNjA3NTgvZmlsZXMvbHJfbmV0d29yay5yZHMiKSkNCiNoZWFkKGxyX25ldHdvcmspDQoNCndlaWdodGVkX25ldHdvcmtzID0gcmVhZFJEUyh1cmwoImh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmQvMzI2MDc1OC9maWxlcy93ZWlnaHRlZF9uZXR3b3Jrcy5yZHMiKSkNCndlaWdodGVkX25ldHdvcmtzX2xyID0gd2VpZ2h0ZWRfbmV0d29ya3MkbHJfc2lnICU+JSBpbm5lcl9qb2luKGxyX25ldHdvcmsgJT4lIGRpc3RpbmN0KGZyb20sdG8pLCBieSA9IGMoImZyb20iLCJ0byIpKQ0KI2hlYWQod2VpZ2h0ZWRfbmV0d29ya3MkbHJfc2lnKSAjIGludGVyYWN0aW9ucyBhbmQgdGhlaXIgd2VpZ2h0cyBpbiB0aGUgbGlnYW5kLXJlY2VwdG9yICsgc2lnbmFsaW5nIG5ldHdvcmsNCg0KDQojIyMjIERlZmluZSBhIHNlbmRlciBhbmQgcmVjZWl2ZXIgcG9wdWxhdGlvbiBhbmQgZGV0ZXJtaW5lIHdoaWNoIGdlbmVzIGFyZSBleHByZXNzZWQgaW4gYm90aCBwb3B1bGF0aW9ucw0KDQojIyByZWNlaXZlcg0KcmVjZWl2ZXIgPSBjKCJDRDEwKyIpDQpleHByZXNzZWRfZ2VuZXNfcmVjZWl2ZXIgPSBnZXRfZXhwcmVzc2VkX2dlbmVzKHJlY2VpdmVyLCBTZXVyYXQuRGF0YSwgcGN0ID0gMC4xMCkNCmJhY2tncm91bmRfZXhwcmVzc2VkX2dlbmVzID0gZXhwcmVzc2VkX2dlbmVzX3JlY2VpdmVyICU+JSAuWy4gJWluJSByb3duYW1lcyhsaWdhbmRfdGFyZ2V0X21hdHJpeCldDQoNCiMjIHNlbmRlcg0Kc2VuZGVyX2NlbGx0eXBlcyA9IGMoIlBhbkNLKyIpDQpsaXN0X2V4cHJlc3NlZF9nZW5lc19zZW5kZXIgPSBzZW5kZXJfY2VsbHR5cGVzICU+JSB1bmlxdWUoKSAlPiUgbGFwcGx5KGdldF9leHByZXNzZWRfZ2VuZXMsIFNldXJhdC5EYXRhLCAwLjEwKSAjIGxhcHBseSB0byBnZXQgdGhlIGV4cHJlc3NlZCBnZW5lcyBvZiBldmVyeSBzZW5kZXIgY2VsbCB0eXBlIHNlcGFyYXRlbHkgaGVyZQ0KZXhwcmVzc2VkX2dlbmVzX3NlbmRlciA9IGxpc3RfZXhwcmVzc2VkX2dlbmVzX3NlbmRlciAlPiUgdW5saXN0KCkgJT4lIHVuaXF1ZSgpDQoNCiMjIyMgSW1wb3J0IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBpbiByZWNpZXZlciBwb3B1bGF0aW9ucw0KZ2VuZXNldC5vaSA8LSB0b3BfZw0KDQojIyMjIyAgRGVmaW5lIGEgc2V0IG9mIGxpZ2FuZHMgaW4gc2VuZGVyIHBvcHVsYXRpb25zDQpsaWdhbmRzID0gbHJfbmV0d29yayAlPiUgcHVsbChmcm9tKSAlPiUgdW5pcXVlKCkNCnJlY2VwdG9ycyA9IGxyX25ldHdvcmsgJT4lIHB1bGwodG8pICU+JSB1bmlxdWUoKQ0KDQpleHByZXNzZWRfbGlnYW5kcyA9IGludGVyc2VjdChsaWdhbmRzLGV4cHJlc3NlZF9nZW5lc19zZW5kZXIpDQpleHByZXNzZWRfcmVjZXB0b3JzID0gaW50ZXJzZWN0KHJlY2VwdG9ycyxleHByZXNzZWRfZ2VuZXNfcmVjZWl2ZXIpDQoNCnBvdGVudGlhbF9saWdhbmRzID0gbHJfbmV0d29yayAlPiUgZmlsdGVyKGZyb20gJWluJSBleHByZXNzZWRfbGlnYW5kcyAmIHRvICVpbiUgZXhwcmVzc2VkX3JlY2VwdG9ycykgJT4lIHB1bGwoZnJvbSkgJT4lIHVuaXF1ZSgpDQoNCg0KIyMjIyMjIyMjIyMjIyMNCiMjIyMjIyMjIyMjIFBlcmZvcm0gTmljaGVuZXQgYW5hbHlzaXMNCg0KDQpsaWdhbmRfYWN0aXZpdGllcyA9IHByZWRpY3RfbGlnYW5kX2FjdGl2aXRpZXMoZ2VuZXNldCA9IGdlbmVzZXQub2ksIGJhY2tncm91bmRfZXhwcmVzc2VkX2dlbmVzID0gYmFja2dyb3VuZF9leHByZXNzZWRfZ2VuZXMsIGxpZ2FuZF90YXJnZXRfbWF0cml4ID0gbGlnYW5kX3RhcmdldF9tYXRyaXgsIHBvdGVudGlhbF9saWdhbmRzID0gcG90ZW50aWFsX2xpZ2FuZHMpDQoNCmxpZ2FuZF9hY3Rpdml0aWVzID0gbGlnYW5kX2FjdGl2aXRpZXMgJT4lIGFycmFuZ2UoLXBlYXJzb24pIA0KI2xpZ2FuZF9hY3Rpdml0aWVzID0gbGlnYW5kX2FjdGl2aXRpZXMgJT4lIGFycmFuZ2UoLXBlYXJzb24pICU+JSBtdXRhdGUocmFuayA9IHJhbmsoZGVzYyhwZWFyc29uKSkpDQoNCg0KIyMjIyMjIyMjIyMjIyMNCiMjIyMjIyMgSGlzdG9ncmFtIG9mIGxpZ2FuZCBhY3Rpdml0aWVzDQoNCnBfaGlzdF9saWdfYWN0aXZpdHkgPSBnZ3Bsb3QobGlnYW5kX2FjdGl2aXRpZXMsIGFlcyh4PXBlYXJzb24pKSArIA0KICBnZW9tX2hpc3RvZ3JhbShjb2xvcj0iYmxhY2siLCBmaWxsPSJkYXJrb3JhbmdlIikgICsgDQogICMgZ2VvbV9kZW5zaXR5KGFscGhhPS4xLCBmaWxsPSJvcmFuZ2UiKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9bWluKGxpZ2FuZF9hY3Rpdml0aWVzICU+JSB0b3BfbigyMCwgcGVhcnNvbikgJT4lIHB1bGwocGVhcnNvbikpKSwgY29sb3I9InJlZCIsIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTEpICsgDQogIGxhYnMoeD0ibGlnYW5kIGFjdGl2aXR5IChQQ0MpIiwgeSA9ICIjIGxpZ2FuZHMiKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KcF9oaXN0X2xpZ19hY3Rpdml0eQ0KDQoNCiMjIyBEb3QgYmxvdCBvZiB0b3AgbGlnYW5kcyBzcGxpdCBieSByZWdpb24NCg0KYmVzdF91cHN0cmVhbV9saWdhbmRzID0gYyhsaWdhbmRfYWN0aXZpdGllcyAlPiUgdG9wX24oMjAsIHBlYXJzb24pICU+JSBhcnJhbmdlKC1wZWFyc29uKSAlPiUgcHVsbCh0ZXN0X2xpZ2FuZCkgJT4lIHVuaXF1ZSgpKQ0KDQojIHRlc3QgPC0gU2V1cmF0LkRhdGENCiMgeWVldCA8LSBjKCJXTlQzQSIpDQojIGRhdGEoInBibWNfc21hbGwiKQ0KIyB0ZXN0W1siUk5BIl1dQHZhci5mZWF0dXJlcyA8LSBiZXN0X3Vwc3RyZWFtX2xpZ2FuZHMNCiMgI2RhdGEuZnJhbWUocm93Lm5hbWVzID0gcm93bmFtZXModGVzdFtbIlJOQSJdXSkpDQojIA0KIyBEb3RQbG90KHRlc3QsIGZlYXR1cmVzID0gYmVzdF91cHN0cmVhbV9saWdhbmRzLCBjb2xzID0gIlJkWWxCdSIpICsgUm90YXRlZEF4aXMoKQ0KDQojIyBlbHNlDQoNCmFjdGl2ZV9saWdhbmRfdGFyZ2V0X2xpbmtzX2RmID0gYmVzdF91cHN0cmVhbV9saWdhbmRzICU+JSBsYXBwbHkoZ2V0X3dlaWdodGVkX2xpZ2FuZF90YXJnZXRfbGlua3MsZ2VuZXNldCA9IGdlbmVzZXQub2ksIGxpZ2FuZF90YXJnZXRfbWF0cml4ID0gbGlnYW5kX3RhcmdldF9tYXRyaXgsIG4gPSAyNTApICU+JSBiaW5kX3Jvd3MoKQ0KYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3MgPSBwcmVwYXJlX2xpZ2FuZF90YXJnZXRfdmlzdWFsaXphdGlvbihsaWdhbmRfdGFyZ2V0X2RmID0gYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYsIGxpZ2FuZF90YXJnZXRfbWF0cml4ID0gbGlnYW5kX3RhcmdldF9tYXRyaXgsIGN1dG9mZiA9IDAuMjUpDQoNCm9yZGVyX2xpZ2FuZHMgPSBpbnRlcnNlY3QoYmVzdF91cHN0cmVhbV9saWdhbmRzLCBjb2xuYW1lcyhhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rcykpICU+JSByZXYoKQ0Kb3JkZXJfdGFyZ2V0cyA9IGFjdGl2ZV9saWdhbmRfdGFyZ2V0X2xpbmtzX2RmJHRhcmdldCAlPiUgdW5pcXVlKCkNCnZpc19saWdhbmRfdGFyZ2V0ID0gYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3Nbb3JkZXJfdGFyZ2V0cyxvcmRlcl9saWdhbmRzXSAlPiUgdCgpDQoNCnBfbGlnYW5kX3RhcmdldF9uZXR3b3JrID0gdmlzX2xpZ2FuZF90YXJnZXQgJT4lIG1ha2VfaGVhdG1hcF9nZ3Bsb3QoIlByaW9yaXRpemVkIFBhbkNLKyBsaWdhbmRzIiwiQ0QxMCsgcG90ZW50aWFsIHJlY2VwdG9yIGNlbGxzIiwgY29sb3IgPSAicHVycGxlIixsZWdlbmRfcG9zaXRpb24gPSAidG9wIiwgeF9heGlzX3Bvc2l0aW9uID0gInRvcCIsbGVnZW5kX3RpdGxlID0gIlJlZ3VsYXRvcnkgcG90ZW50aWFsIikgKyBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAid2hpdGVzbW9rZSIsICBoaWdoID0gInB1cnBsZSIsIGJyZWFrcyA9IGMoMCwwLjAwNSwwLjAxKSkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChmYWNlID0gIml0YWxpYyIpKQ0KDQpwX2xpZ2FuZF90YXJnZXRfbmV0d29yaw0KDQojIyBjaXJjb3MNCg0KIyBRQyBvbiBxdWFudGlsZQ0KY3V0b2ZmX2luY2x1ZGVfYWxsX2xpZ2FuZHMgPSBhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZiR3ZWlnaHQgJT4lIHF1YW50aWxlKDAuNjYpDQphY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZl9jaXJjb3MgPSBhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZiAlPiUgZmlsdGVyKHdlaWdodCA+IGN1dG9mZl9pbmNsdWRlX2FsbF9saWdhbmRzKQ0KDQpsaWdhbmRzX3RvX3JlbW92ZSA9IHNldGRpZmYoYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYkbGlnYW5kICU+JSB1bmlxdWUoKSwgYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGZfY2lyY29zJGxpZ2FuZCAlPiUgdW5pcXVlKCkpDQp0YXJnZXRzX3RvX3JlbW92ZSA9IHNldGRpZmYoYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYkdGFyZ2V0ICU+JSB1bmlxdWUoKSwgYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGZfY2lyY29zJHRhcmdldCAlPiUgdW5pcXVlKCkpDQogIA0KY2lyY29zX2xpbmtzID0gYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYgJT4lIGZpbHRlcighdGFyZ2V0ICVpbiUgdGFyZ2V0c190b19yZW1vdmUgJiFsaWdhbmQgJWluJSBsaWdhbmRzX3RvX3JlbW92ZSkNCg0KIyBDb2xvcg0KZ3JpZF9jb2xfbGlnYW5kID1jKCJQYW5DSysiID0gInJlZCINCiAgICAgICAgICAgICAgICAgICMsIkdlbmVyYWwiID0gImdyZWVuIg0KICAgICAgICAgICAgICAgICAgICkNCmdyaWRfY29sX3RhcmdldCA9YygiQ0QxMCsiID0gImdyZWVuIikNCg0KZ3JpZF9jb2xfdGJsX2xpZ2FuZCA9IHRpYmJsZShsaWdhbmRfdHlwZSA9IGdyaWRfY29sX2xpZ2FuZCAlPiUgbmFtZXMoKSwgY29sb3JfbGlnYW5kX3R5cGUgPSBncmlkX2NvbF9saWdhbmQpDQpncmlkX2NvbF90YmxfdGFyZ2V0ID0gdGliYmxlKHRhcmdldF90eXBlID0gZ3JpZF9jb2xfdGFyZ2V0ICU+JSBuYW1lcygpLCBjb2xvcl90YXJnZXRfdHlwZSA9IGdyaWRfY29sX3RhcmdldCkNCg0KY2lyY29zX2xpbmtzID0gY2lyY29zX2xpbmtzICU+JSBtdXRhdGUobGlnYW5kID0gcGFzdGUobGlnYW5kLCIgIikpICMgZXh0cmEgc3BhY2U6IG1ha2UgYSBkaWZmZXJlbmNlIGJldHdlZW4gYSBnZW5lIGFzIGxpZ2FuZCBhbmQgYSBnZW5lIGFzIHRhcmdldCENCmNpcmNvc19saW5rcyA9IGNpcmNvc19saW5rcyAlPiUgaW5uZXJfam9pbihncmlkX2NvbF90YmxfbGlnYW5kLCBieSA9IGNoYXJhY3RlcigpKSAlPiUgaW5uZXJfam9pbihncmlkX2NvbF90YmxfdGFyZ2V0LCBieSA9IGNoYXJhY3RlcigpKQ0KbGlua3NfY2lyY2xlID0gY2lyY29zX2xpbmtzICU+JSBzZWxlY3QobGlnYW5kLHRhcmdldCwgd2VpZ2h0KQ0KDQpsaWdhbmRfY29sb3IgPSBjaXJjb3NfbGlua3MgJT4lIGRpc3RpbmN0KGxpZ2FuZCxjb2xvcl9saWdhbmRfdHlwZSkNCmdyaWRfbGlnYW5kX2NvbG9yID0gbGlnYW5kX2NvbG9yJGNvbG9yX2xpZ2FuZF90eXBlICU+JSBzZXRfbmFtZXMobGlnYW5kX2NvbG9yJGxpZ2FuZCkNCnRhcmdldF9jb2xvciA9IGNpcmNvc19saW5rcyAlPiUgZGlzdGluY3QodGFyZ2V0LGNvbG9yX3RhcmdldF90eXBlKQ0KZ3JpZF90YXJnZXRfY29sb3IgPSB0YXJnZXRfY29sb3IkY29sb3JfdGFyZ2V0X3R5cGUgJT4lIHNldF9uYW1lcyh0YXJnZXRfY29sb3IkdGFyZ2V0KQ0KDQpncmlkX2NvbCA9IGMoZ3JpZF9saWdhbmRfY29sb3IsZ3JpZF90YXJnZXRfY29sb3IpDQoNCiMgZ2l2ZSB0aGUgb3B0aW9uIHRoYXQgbGlua3MgaW4gdGhlIGNpcmNvcyBwbG90IHdpbGwgYmUgdHJhbnNwYXJhbnQgfiBsaWdhbmQtdGFyZ2V0IHBvdGVudGlhbCBzY29yZQ0KdHJhbnNwYXJlbmN5ID0gY2lyY29zX2xpbmtzICU+JSBtdXRhdGUod2VpZ2h0ID0od2VpZ2h0LW1pbih3ZWlnaHQpKS8obWF4KHdlaWdodCktbWluKHdlaWdodCkpKSAlPiUgbXV0YXRlKHRyYW5zcGFyZW5jeSA9IDEtd2VpZ2h0KSAlPiUgLiR0cmFuc3BhcmVuY3kgDQoNCiMgU3BlY2lmaWMgb3JkZXIgd2l0aCBnYXBzIGlmIG5lZWRlZA0KdGFyZ2V0X29yZGVyID0gY2lyY29zX2xpbmtzJHRhcmdldCAlPiUgdW5pcXVlKCkNCiNsaWdhbmRfb3JkZXIgPSBjKENBRl9zcGVjaWZpY19saWdhbmRzLGdlbmVyYWxfbGlnYW5kcyxlbmRvdGhlbGlhbF9zcGVjaWZpY19saWdhbmRzKSAlPiUgYyhwYXN0ZSguLCIgIikpICU+JSBpbnRlcnNlY3QoY2lyY29zX2xpbmtzJGxpZ2FuZCkNCmxpZ2FuZF9vcmRlciA9IGMoKSAlPiUgYyhwYXN0ZSguLCIgIikpICU+JSBpbnRlcnNlY3QoY2lyY29zX2xpbmtzJGxpZ2FuZCkNCm9yZGVyID0gYyhsaWdhbmRfb3JkZXIsdGFyZ2V0X29yZGVyKQ0KDQp3aWR0aF9zYW1lX2NlbGxfc2FtZV9saWdhbmRfdHlwZSA9IDAuNQ0Kd2lkdGhfZGlmZmVyZW50X2NlbGwgPSA2DQp3aWR0aF9saWdhbmRfdGFyZ2V0ID0gMTUNCndpZHRoX3NhbWVfY2VsbF9zYW1lX3RhcmdldF90eXBlID0gMC41DQoNCmdhcHMgPSBjKA0KICAjIHdpZHRoX2xpZ2FuZF90YXJnZXQsDQogIHJlcCh3aWR0aF9zYW1lX2NlbGxfc2FtZV9saWdhbmRfdHlwZSwgdGltZXMgPSAoY2lyY29zX2xpbmtzICU+JSBmaWx0ZXIobGlnYW5kX3R5cGUgPT0gIlBhbkNLKyIpICU+JSBkaXN0aW5jdChsaWdhbmQpICU+JSBucm93KCkgLTEpKSwNCiAgd2lkdGhfZGlmZmVyZW50X2NlbGwsDQogICMgcmVwKHdpZHRoX3NhbWVfY2VsbF9zYW1lX2xpZ2FuZF90eXBlLCB0aW1lcyA9IChjaXJjb3NfbGlua3MgJT4lIGZpbHRlcihsaWdhbmRfdHlwZSA9PSAiR2VuZXJhbCIpICU+JSBkaXN0aW5jdChsaWdhbmQpICU+JSBucm93KCkgLTEpKSwNCiAgIyB3aWR0aF9kaWZmZXJlbnRfY2VsbCwNCiAgI3JlcCh3aWR0aF9zYW1lX2NlbGxfc2FtZV9saWdhbmRfdHlwZSwgdGltZXMgPSAoY2lyY29zX2xpbmtzICU+JSBmaWx0ZXIobGlnYW5kX3R5cGUgPT0gIkVuZG90aGVsaWFsLXNwZWNpZmljIikgJT4lIGRpc3RpbmN0KGxpZ2FuZCkgJT4lIG5yb3coKSAtMSkpLCANCiAgI3dpZHRoX2xpZ2FuZF90YXJnZXQsDQogIHJlcCh3aWR0aF9zYW1lX2NlbGxfc2FtZV90YXJnZXRfdHlwZSwgdGltZXMgPSAoY2lyY29zX2xpbmtzICU+JSBmaWx0ZXIodGFyZ2V0X3R5cGUgPT0gIkNEMTArIikgJT4lIGRpc3RpbmN0KHRhcmdldCkgJT4lIG5yb3coKSAtMSkpLA0KICB3aWR0aF9saWdhbmRfdGFyZ2V0DQogICkNCg0KI2NpcmNvcy5wYXIoZ2FwLmRlZ3JlZSA9IGdhcHMpDQoNCmNpcmNvcy5wYXIoUkVTRVQgPSBUUlVFKQ0KDQpjaG9yZERpYWdyYW0obGlua3NfY2lyY2xlLCANCiAgICAgICAgICAgICBkaXJlY3Rpb25hbCA9IDEsDQogICAgICAgICAgICAgI29yZGVyPW9yZGVyLA0KICAgICAgICAgICAgIGxpbmsuc29ydCA9IFRSVUUsIA0KICAgICAgICAgICAgIGxpbmsuZGVjcmVhc2luZyA9IEZBTFNFLCANCiAgICAgICAgICAgICBncmlkLmNvbCA9IGdyaWRfY29sLA0KICAgICAgICAgICAgIHRyYW5zcGFyZW5jeSA9IHRyYW5zcGFyZW5jeSwgDQogICAgICAgICAgICAgZGlmZkhlaWdodCA9IDAuMDA1LCANCiAgICAgICAgICAgICBkaXJlY3Rpb24udHlwZSA9IGMoImRpZmZIZWlnaHQiLCAiYXJyb3dzIiksDQogICAgICAgICAgICAgbGluay5hcnIudHlwZSA9ICJiaWcuYXJyb3ciLCANCiAgICAgICAgICAgICBsaW5rLnZpc2libGUgPSBsaW5rc19jaXJjbGUkd2VpZ2h0ID49IGN1dG9mZl9pbmNsdWRlX2FsbF9saWdhbmRzLA0KICAgICAgICAgICAgIGFubm90YXRpb25UcmFjayA9ICJncmlkIiwNCiAgICAgICAgICAgICBwcmVBbGxvY2F0ZVRyYWNrcyA9IGxpc3QodHJhY2suaGVpZ2h0ID0gMC4wNzUpKQ0KICAgICAgICAgICAgICNwcmVBbGxvY2F0ZVRyYWNrcyA9IGxpc3QodHJhY2suaGVpZ2h0ID0gbWF4KHN0cndpZHRoKHVubGlzdChkaW1uYW1lcyhsaW5rc19jaXJjbGUpKSkpKSkgIA0KICAgICAgICAgICAgIA0KIyB3ZSBnbyBiYWNrIHRvIHRoZSBmaXJzdCB0cmFjayBhbmQgY3VzdG9taXplIHNlY3RvciBsYWJlbHMNCmNpcmNvcy50cmFjayh0cmFjay5pbmRleCA9IDEsIHBhbmVsLmZ1biA9IGZ1bmN0aW9uKHgsIHkpIHsNCiAgICBjaXJjb3MudGV4dChDRUxMX01FVEEkeGNlbnRlciwgQ0VMTF9NRVRBJHlsaW1bMV0sIENFTExfTUVUQSRzZWN0b3IuaW5kZXgsDQogICAgICAgIGZhY2luZyA9ICJjbG9ja3dpc2UiLCBuaWNlRmFjaW5nID0gVFJVRSwgYWRqID0gYygwLCAwLjU1KSwgY2V4ID0gMSkNCn0sIGJnLmJvcmRlciA9IE5BKSANCg0KbGVnZW5kKCJib3R0b21sZWZ0IiwgDQogICAgICAgdGl0bGUgPSAiQ2VsbCBzcGVjaWZpYyIsDQogICAgICAgY2V4PSAzLCANCiAgICAgICBsZWdlbmQgPSB1bmlxdWUoYyhjaXJjb3NfbGlua3MkbGlnYW5kX3R5cGUsIHVuaXF1ZShjaXJjb3NfbGlua3MkdGFyZ2V0X3R5cGUpKSksDQogICAgICAgZmlsbCA9IHVuaXF1ZShjKGdyaWRfY29sKSkpDQp0aXRsZSgiTGlnYW5kLXJlY2VwdG9yIGNvbm5lY3Rpb25zIikNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTI1LGZpZy5oZWlnaHQ9MTB9DQojIGdldCB0aGUgbGlnYW5kLXJlY2VwdG9yIG5ldHdvcmsgb2YgdGhlIHRvcC1yYW5rZWQgbGlnYW5kcw0KbHJfbmV0d29ya190b3AgPSBscl9uZXR3b3JrICU+JSBmaWx0ZXIoZnJvbSAlaW4lIGJlc3RfdXBzdHJlYW1fbGlnYW5kcyAmIHRvICVpbiUgZXhwcmVzc2VkX3JlY2VwdG9ycykgJT4lIGRpc3RpbmN0KGZyb20sdG8pDQpiZXN0X3Vwc3RyZWFtX3JlY2VwdG9ycyA9IGxyX25ldHdvcmtfdG9wICU+JSBwdWxsKHRvKSAlPiUgdW5pcXVlKCkNCg0KIyAjIGdldCB0aGUgd2VpZ2h0cyBvZiB0aGUgbGlnYW5kLXJlY2VwdG9yIGludGVyYWN0aW9ucyBhcyB1c2VkIGluIHRoZSBOaWNoZU5ldCBtb2RlbA0KIyB3ZWlnaHRlZF9uZXR3b3JrcyA9IHJlYWRSRFModXJsKCJodHRwczovL3plbm9kby5vcmcvcmVjb3JkLzMyNjA3NTgvZmlsZXMvd2VpZ2h0ZWRfbmV0d29ya3MucmRzIikpDQpscl9uZXR3b3JrX3RvcF9kZiA9IHdlaWdodGVkX25ldHdvcmtzJGxyX3NpZyAlPiUgZmlsdGVyKGZyb20gJWluJSBiZXN0X3Vwc3RyZWFtX2xpZ2FuZHMgJiB0byAlaW4lIGJlc3RfdXBzdHJlYW1fcmVjZXB0b3JzKQ0KDQojIGNvbnZlcnQgdG8gYSBtYXRyaXgNCmxyX25ldHdvcmtfdG9wX2RmID0gbHJfbmV0d29ya190b3BfZGYgJT4lIHNwcmVhZCgiZnJvbSIsIndlaWdodCIsZmlsbCA9IDApDQpscl9uZXR3b3JrX3RvcF9tYXRyaXggPSBscl9uZXR3b3JrX3RvcF9kZiAlPiUgc2VsZWN0KC10bykgJT4lIGFzLm1hdHJpeCgpICU+JSBtYWdyaXR0cjo6c2V0X3Jvd25hbWVzKGxyX25ldHdvcmtfdG9wX2RmJHRvKQ0KDQojIHBlcmZvcm0gaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgdG8gb3JkZXIgdGhlIGxpZ2FuZHMgYW5kIHJlY2VwdG9ycw0KZGlzdF9yZWNlcHRvcnMgPSBkaXN0KGxyX25ldHdvcmtfdG9wX21hdHJpeCwgbWV0aG9kID0gImJpbmFyeSIpDQpoY2x1c3RfcmVjZXB0b3JzID0gaGNsdXN0KGRpc3RfcmVjZXB0b3JzLCBtZXRob2QgPSAid2FyZC5EMiIpDQpvcmRlcl9yZWNlcHRvcnMgPSBoY2x1c3RfcmVjZXB0b3JzJGxhYmVsc1toY2x1c3RfcmVjZXB0b3JzJG9yZGVyXQ0KDQpkaXN0X2xpZ2FuZHMgPSBkaXN0KGxyX25ldHdvcmtfdG9wX21hdHJpeCAlPiUgdCgpLCBtZXRob2QgPSAiYmluYXJ5IikNCmhjbHVzdF9saWdhbmRzID0gaGNsdXN0KGRpc3RfbGlnYW5kcywgbWV0aG9kID0gIndhcmQuRDIiKQ0Kb3JkZXJfbGlnYW5kc19yZWNlcHRvciA9IGhjbHVzdF9saWdhbmRzJGxhYmVsc1toY2x1c3RfbGlnYW5kcyRvcmRlcl0NCg0KDQp2aXNfbGlnYW5kX3JlY2VwdG9yX25ldHdvcmsgPSBscl9uZXR3b3JrX3RvcF9tYXRyaXhbb3JkZXJfcmVjZXB0b3JzLCBvcmRlcl9saWdhbmRzX3JlY2VwdG9yXQ0KcF9saWdhbmRfcmVjZXB0b3JfbmV0d29yayA9IHZpc19saWdhbmRfcmVjZXB0b3JfbmV0d29yayAlPiUgdCgpICU+JSBtYWtlX2hlYXRtYXBfZ2dwbG90KCJQcmlvcml0aXplZCBQYW5DSytsaWdhbmRzIiwiUmVjZXB0b3JzIGV4cHJlc3NlZCBieSBDRDEwKyIsIGNvbG9yID0gIm1lZGl1bXZpb2xldHJlZCIsIHhfYXhpc19wb3NpdGlvbiA9ICJ0b3AiLGxlZ2VuZF90aXRsZSA9ICJQcmlvciBpbnRlcmFjdGlvbiBwb3RlbnRpYWwiKQ0KcF9saWdhbmRfcmVjZXB0b3JfbmV0d29yaw0KYGBgDQoNCiMgQ05WDQoNCmBgYHtyfQ0KI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJpbmZlcmNudiIpDQpsaWJyYXJ5KGluZmVyY252KQ0KI2RldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiYWVyaWNrc28vU3BhdGlhbEluZmVyQ05WIikNCiNsaWJyYXJ5KFNwYXRpYWxJbmZlckNOVikNCmBgYA0KDQpgYGB7ciBjcmVhdGUgY252IGZpbGVzX30NCmZpbGUgPC0gcmVhZExpbmVzKFBLQ0ZpbGVzKQ0KZmlsZSA8LSBnc3ViKCcgJywgJycsDQogICAgICAgIGdzdWIoJyInLCAnJywgZmlsZSkpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEZvcm1hdCBhbmQgcmVtb3ZlIHNwYWNlDQoNCmRpc3BsYXkgPC0gZmlsZVtncmVwbCgiRGlzcGxheU5hbWUiLCBmaWxlLCBmaXhlZCA9IFRSVUUpXSAgICAgICAgICMgR3JhYiBkaXNwbGF5IG5hbWVzDQpkaXNwbGF5IDwtIGdzdWIoJ0Rpc3BsYXlOYW1lJywgIiIsDQogICAgICAgICAgIGdzdWIoJyInLCAiIiwNCiAgICAgICAgICAgZ3N1YignOicsICIiLA0KICAgICAgICAgICBnc3ViKCcsJywgIiIsDQogICAgICAgICAgIGdzdWIoJyAnLCAiIiwNCiAgICAgICAgICAgZ3N1YignXzAxJywgIiIsIGRpc3BsYXkpKSkpKSkgICAgICAgICAgICAgICAgICAgICAgICAgICMgR3JhYiBvbmx5IHRoZSBuYW1lcw0KZGlzcGxheSA8LSB1bmlxdWUoZGlzcGxheSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSZW1vdmUgZHVwbGljYXRlcw0KDQpjaHIgPC0gZmlsZVtncmVwKCJHZW5vbWVDb29yZGluYXRlcyIsIGZpbGUpKzFdICAgICAgICAgICAgICAgICAgICAjIEdldCB0aGUgY2hyIHBvc2l0aW9ucyB1bmRlciB0aGUgR2Vub21lQ29vcmRpbmF0ZXMgbGluZQ0KY2hyIDwtZ3N1YignLCcsICIiLCBjaHIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSZW1vdmUgdW53YW50ZWQgc3ltYm9scw0KDQpwb3NpdGlvbnMgPC0gZGF0YS5mcmFtZSgNCiAgbmFtZSA9IGRpc3BsYXksIA0KICBjaHIgPSBjaHIpDQpwb3NpdGlvbnMgPC0gcG9zaXRpb25zWyFncmVwbCgiVGFyZ2V0U2VxdWVuY2UiLCBwb3NpdGlvbnMkY2hyKSxdICAjIFJlbW92ZSBlbnRyaWVzIHdpdGhvdXQgY29vcmRpbmF0ZXMNCg0KcG9zaXRpb25zJGNociA8LSBnc3ViKCc6JywgJy0nLCBwb3NpdGlvbnMkY2hyKSAgICAgICAgICAgICAgICAgICAgIyBGb3JtYXQgZm9yIHNwbGl0dGluZw0KcG9zaXRpb25zJGNociA8LSBzdHJfc3BsaXRfZml4ZWQocG9zaXRpb25zJGNociwgIi0iLCAzKSAgICAgICAgICAgIyBTcGxpdCBpbnRvIGNociwgYmVnaW4sIGFuZCBlbmQgcG9zaXRpb24NCnBvc2l0aW9ucyA8LSBhcy5tYXRyaXgocG9zaXRpb25zKSANCnBvc2l0aW9ucyA8LSBhcy5kYXRhLmZyYW1lKHBvc2l0aW9ucykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU3dhcCB0eXBlcyB0byByZWNvZ25pemUgc3BsaXQgY29sdW1ucyBhcyBzaW5ndWxhciBjb2x1bW5zDQpjb2xuYW1lcyhwb3NpdGlvbnMpIDwtIE5VTEwNCnJvd25hbWVzKHBvc2l0aW9ucykgPC0gTlVMTCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW5mZXJDTlYgcmVxdWlyZXMgbm8gY29sdW1uIGFuZCByb3cgbmFtZXMNCg0Kd3JpdGUudGFibGUocG9zaXRpb25zLCAiZ2VuZV9vcmRlcl9Ic19XVEFfdjFfcGtjLnR4dCIsIA0KICAgICAgICAgICAgc2VwID0gIlx0IiwNCiAgICAgICAgICAgIHF1b3RlID0gRkFMU0UsIA0KICAgICAgICAgICAgY29sLm5hbWVzID0gRkFMU0UsIA0KICAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBXcml0ZSBhd2F5DQoNCmNvdW50cyA8LSB0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJleHBycyJdXQ0Kd3JpdGUudGFibGUoY291bnRzLCAiQ291bnRzLnRzdiIsIHNlcCA9ICJcdCIpICAgICAgICAgICAgICAgICAgICAjIE1ha2UgcmF3IGNvdW50IGZpbGUNCg0KYW5ub3RhdGlvbiA8LSB0YXJnZXRfRGF0YUBwaGVub0RhdGFAZGF0YQ0KYW5ub3RhdGlvbiRTYW1wbGVJRCA8LSByb3duYW1lcyhhbm5vdGF0aW9uKQ0KYW5ub3RhdGlvbiA8LSBhbm5vdGF0aW9uW2MoIlNhbXBsZUlEIiwgIkFOTjIiKV0gICAgICAgICAgICAgICAgICAjIFNlbGVjdCBTYW1wbGVJRCBhbmQgdGhlIG5lZWRlZCBhbm5vdGF0aW9uIChDTlYgcmVxdWlyZXMgU2FtcGxlSUQgYW5kIG9ubHkgMSBhbm5vdGF0aW9uKQ0Kcm93bmFtZXMoYW5ub3RhdGlvbikgPC0gTlVMTA0KY29sbmFtZXMoYW5ub3RhdGlvbikgPC0gTlVMTCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluZmVyQ05WIHJlcXVpcmVzIG5vIGNvbHVtbiBhbmQgcm93IG5hbWVzDQp3cml0ZS50YWJsZShhbm5vdGF0aW9uLCAiQW5ub3RhdGlvbnMudHN2IiwgDQogICAgICAgICAgICBzZXAgPSAiXHQiLA0KICAgICAgICAgICAgcXVvdGUgPSBGQUxTRSwgDQogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwgDQogICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWFrZSBhbm5vdGF0aW9uIGZpbGUNCmBgYA0KDQoNCmBgYHtyIHJ1biBDTlZfLCBpbmNsdWRlPUZBTFNFfQ0KIyBQaWNrIG5vcm1hbC9yZWZlcmVuY2UgZ3JvdXAocykNCnJlZmVyZW5jZSA8LSBjKCJDb3J0aWNhbCBnbG9tZXJ1bHVzIikNCg0KIyBOYW1lIG91dHB1dCBmb2xkZXINCm91dF9kaXIgPC0gIkNOViINCg0KIyBDcmVhdGUgdGhlIGluZmVyY252IG9iamVjdA0KaW5mZXJjbnZfb2JqID0gQ3JlYXRlSW5mZXJjbnZPYmplY3QocmF3X2NvdW50c19tYXRyaXg9ICJDb3VudHMudHN2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25zX2ZpbGU9ICJBbm5vdGF0aW9ucy50c3YiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVsaW09Ilx0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfb3JkZXJfZmlsZT0gImdlbmVfb3JkZXJfSHNfV1RBX3YxX3BrYy50eHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI2dlbmVfb3JkZXJfZmlsZT0gImdlbmNvZGVfdjE5X2dlbmVfcG9zLnR4dCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWZfZ3JvdXBfbmFtZXM9IHJlZmVyZW5jZSwgIyBpbnB1dCB0aGUgbm9ybWFsL3JlZmVyZW5jZSBncm91cCBuYW1lcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hyX2V4Y2x1ZGUgPSBjKCJjaHJNIikpICMgRGVmYXVsdCBleGNsdWRlcyBjaHJYLCBjaHJZIGFuZCBjaHJNLiBCeSBvbmx5IHBpY2tpbmcgY2hyTSB5b3UgaW5jbHVkZSB0aGUgWCBhbmQgWSBjaHJvbW9zb21lcy4gICAgIA0KDQojIHBlcmZvcm0gaW5mZXJjbnYgb3BlcmF0aW9ucyB0byByZXZlYWwgY252IHNpZ25hbC4gRm9yIGFsbCBvcHRpb25zOiBodHRwczovL3JkcnIuaW8vZ2l0aHViL2Jyb2FkaW5zdGl0dXRlL2luZmVyY252L21hbi9ydW4uaHRtbA0KaW5mZXJjbnZfb2JqID0gaW5mZXJjbnY6OnJ1bihpbmZlcmNudl9vYmosDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1dG9mZj0wLjEsICAjIHVzZSAxIGZvciBzbWFydC1zZXEsIDAuMSBmb3IgMTB4LWdlbm9taWNzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG91dF9kaXI9IG91dF9kaXIsICAjIGRpciBpcyBhdXRvLWNyZWF0ZWQgZm9yIHN0b3Jpbmcgb3V0cHV0cw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2J5X2dyb3Vwcz1GLCAgICMgY2x1c3Rlcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZW5vaXNlPVQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhNTT1GLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmFseXNpc19tb2RlID0gInN1YmNsdXN0ZXJzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyxkZWJ1Zz1UUlVFDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCg0KICAgICAgICAgICMgIkdlbm9tZUNvb3JkaW5hdGVzIjogWw0KICAgICAgICAgICMgICAiY2hyMTM6MTEyMzc4NzMxLTExMjM5MzA2OSIsIA0KICAgICAgICAgICMgICAiY2hyMTM6MTEyMzg3Mzk2LTExMjM5MzA2OSIsIA0KICAgICAgICAgICMgICAiY2hyMTM6MTEyMzc2NDc5LTExMjM5MzA2OSINCmBgYA0KDQpgYGB7ciBzaG93IENOVl8sIGZpZy5oZWlnaHQ9MzAsIGZpZy53aWR0aD0yNX0NCmltZyA8LSByZWFkUE5HKHBhc3RlKG91dF9kaXIsICIvaW5mZXJjbnYucG5nIiwgc2VwID0gIiIpKQ0KZ3JpZDo6Z3JpZC5uZXdwYWdlKCkNCmdyaWQ6OmdyaWQucmFzdGVyKGltZykNCmBgYA0KDQo=